multi-servers for wordpress multisites

Full Scaling Guide for WordPress Multisite Subdomain SaaS (Multi-Server Architecture) Part 1

Q: I run a SaaS website building service built on WordPress Multisite. We use *.abcdomain.com as a wildcard subdomain pattern, where each subdomain maps to a corresponding subsite in the network. Our customers can also map their own custom domains to their assigned subdomain and subsite.

Now that our subsite count is growing and we need to scale out with an additional server, I want to keep our existing *.abcdomain.com wildcard DNS setup intact to handle automatic resolution for all customer domains. What is the proper way to implement this architecture?

multi-servers for wordpress multisites

Core Key Takeaway

You can fully retain your existing *.abcdomain.com wildcard DNS logic and your tenants’ custom domain CNAME setup — no changes are required for end users. The core of scaling your infrastructure is to evolve from a single-server setup to a layered cluster architecture: unified load balancing entry + multiple stateless application servers + shared database + shared file storage. All domain requests are received by the load balancer and distributed evenly across your backend WordPress servers.

WordPress Multisite identifies sites based solely on the Host header in the HTTP request, which maps to the domain column in the wp_blogs database table. This logic is completely independent of server count or IP addresses. As long as the correct Host header is passed through to backend servers, all nodes will correctly resolve the corresponding subsite.


I. Overall Architecture After Upgrade

Custom Tenant Domain (CNAME to xxx.domain.com)
        ↓
*.domain.com Wildcard DNS → Load Balancer (unified entry, SSL termination, traffic distribution)
        ↓
┌─────────────────┬─────────────────┐
│ WP Server 1     │ WP Server 2     │  (infinitely horizontally scalable)
│ Web + PHP       │ Web + PHP       │
└─────────────────┴─────────────────┘
        ↓
   Shared Database + Shared File Storage

Impact on Your Existing Workflow

  • Wildcard DNS: You only need to update the A record for *.domain.com from your original server IP to the load balancer’s public IP. The wildcard resolution logic remains fully intact.
  • Custom tenant domains: Since users already CNAME their domains to xxx.domain.com, they will automatically resolve to the new load balancer endpoint. Zero changes required on the user side, fully transparent to tenants.
  • WordPress configuration: All core Multisite settings and domain mapping rules (native or plugin-based) stay exactly the same. You only need to update the database host value.

II. Step-by-Step Implementation

Step 1: Deploy a Dedicated Shared Database

All subsite settings, content, user data, and domain mapping records for WordPress Multisite are stored in a single database. This shared database is the foundation that allows all application servers to consistently identify sites.

Implementation Steps

  1. Migrate your full MySQL/MariaDB database from the original server to a dedicated database instance. For production SaaS use, a managed service like Amazon RDS, Google Cloud SQL, or Azure Database for MySQL is recommended to eliminate manual database运维 overhead. Ensure the database version matches your existing deployment.
  2. Create a database user with minimal required privileges, and grant access only from the private IP addresses of your WordPress application servers.
  3. Update wp-config.php on all WP servers to point DB_HOST to the private endpoint of your shared database. Leave all other Multisite constants unchanged:
   // Only update this line; all other Multisite configuration stays unchanged
   define( 'DB_HOST', '10.0.1.100:3306' ); 

   // Keep all existing Multisite constants as-is
   define('MULTISITE', true);
   define('SUBDOMAIN_INSTALL', true);
   define('DOMAIN_CURRENT_SITE', 'domain.com');
   define('PATH_CURRENT_SITE', '/');
   define('SITE_ID_CURRENT_SITE', 1);
   define('BLOG_ID_CURRENT_SITE', 1);
  1. Validate the setup with a single server first: confirm all subsites, wp-admin access, and custom domain routing work correctly with the new database.

Step 2: Deploy the Load Balancing Layer (Core for Preserving Wildcard DNS)

The load balancer acts as the single entry point for all traffic. It receives all domain requests and distributes them evenly across your backend WordPress servers.

Solution Comparison

SolutionAdvantagesUse Case
Cloud-managed load balancer (AWS ALB, GCP Load Balancing, Cloudflare Load Balancer)Built-in health checks, auto-scaling, native SNI support, minimal operational overheadRecommended for production SaaS deployments
Self-hosted reverse proxy (Nginx / Caddy)Full control, lower infrastructure costTeams with DevOps capacity, smaller server clusters

Critical Configuration Requirements (mandatory for all solutions)

  1. DNS Update
    Point the *.domain.com wildcard A record to the public IP of your load balancer. Tenant custom domains will automatically follow the CNAME chain to the new endpoint with no user action required.
  2. Request Header Passthrough (Critical)
    You must preserve the original HTTP Host header and client metadata, otherwise WordPress cannot resolve the correct subsite and will not capture real user IPs:
  • Preserve Host header: proxy_set_header Host $host;
  • Forward real client IP: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  • Forward connection protocol: proxy_set_header X-Forwarded-Proto $scheme;
  1. Unified SSL Termination
    It is recommended to terminate HTTPS at the load balancer layer, with plain HTTP traffic between the load balancer and backend servers. This reduces CPU overhead on application servers:
  • Deploy your *.domain.com wildcard SSL certificate directly on the load balancer.
  • For custom tenant domains: Use an SNI-compatible load balancer to manage certificates at scale. For large numbers of custom domains, Caddy with on-demand TLS is ideal — it automatically issues free Let’s Encrypt certificates for any custom domain that hits the server, fully automating HTTPS for SaaS tenants.

Self-Hosted Nginx Load Balancer Example

# Define backend WordPress server pool
upstream wp_saas_cluster {
    server 10.0.2.10:80 weight=1 max_fails=2 fail_timeout=30s; # Server 1
    server 10.0.2.11:80 weight=1 max_fails=2 fail_timeout=30s; # Server 2
    # Add more servers here for future scaling
}

# HTTP to HTTPS redirect
server {
    listen 80;
    server_name *.domain.com;
    return 301 https://$host$request_uri;
}

# HTTPS server block
server {
    listen 443 ssl http2;
    server_name *.domain.com;

    # Wildcard SSL certificate
    ssl_certificate /path/to/wildcard_domain_com.pem;
    ssl_certificate_key /path/to/wildcard_domain_com.key;

    location / {
        proxy_pass http://wp_saas_cluster;
        # Critical: Preserve Host header for WordPress subsite resolution
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_connect_timeout 30s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Critical Fix: Resolve Redirect Loops After SSL Termination

When SSL is terminated at the load balancer, backend servers receive requests over HTTP and may incorrectly redirect to HTTPS, causing an infinite loop. Add the following to wp-config.php on all WP servers:

// Detect HTTPS based on the header passed from the load balancer
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https' ) {
    $_SERVER['HTTPS'] = 'on';
    $_SERVER['SERVER_PORT'] = '443';
}

Step 3: Provision Identical Application Servers

All new WordPress servers must have an identical environment to your original server to avoid compatibility issues.

  1. Unified environment: Install the same version of web server (Nginx/Apache), PHP version, and PHP extensions. Match php.ini and virtual host configurations exactly across all nodes.
  2. Codebase sync: Copy the full WordPress core, themes, plugins, wp-config.php, and sunrise.php (if used) from your original server to all new nodes.
  3. Single-node validation: Test each new server individually via a local hosts file modification before adding it to the load balancer pool, to confirm all subsites and wp-admin work correctly.

Step 4: Ensure File Consistency Across Servers (Most Common Pitfall)

All servers must have identical media uploads, plugin versions, and theme files. Without this, users will see 404 errors for images or encounter plugin version mismatches depending on which server they hit.

Option 1: Object Storage (Top recommendation for SaaS)

  • How it works: Use plugins like WP Offload Media or WP-Stateless to automatically sync all uploaded files to object storage (Amazon S3, Google Cloud Storage, DigitalOcean Spaces, Backblaze B2). Static assets are served directly from the storage bucket or CDN, bypassing application servers entirely.
  • Advantages: No dependency on local file systems — new servers require zero file sync for uploads, enabling true unlimited horizontal scaling. It also drastically reduces I/O load on application servers and improves global load times.
  • Note: Bulk migrate your existing upload library to object storage first, and configure the plugin to rewrite asset URLs sitewide.

Option 2: NFS Shared Storage (For small to medium clusters)

  • How it works: Deploy a dedicated NFS file server to share the wp-content/uploads directory. All WP servers mount this shared path to their local wp-content/uploads directory.
  • Advantages: Simple to configure, fully transparent to WordPress with no code changes required.
  • Disadvantages: The NFS server is a single point of failure, and I/O performance degrades as you add more servers. Best for clusters of 3 nodes or fewer.

Client mount example:

# Install NFS client
apt install nfs-common  # Ubuntu/Debian
yum install nfs-utils   # RHEL/CentOS

# Temporary mount
mount -t nfs 10.0.3.20:/data/wp_uploads /var/www/html/wp-content/uploads

# Persist mount on boot - add to /etc/fstab
echo "10.0.3.20:/data/wp_uploads /var/www/html/wp-content/uploads nfs defaults,_netdev 0 0" >> /etc/fstab

Option 3: Real-Time File Sync (Temporary solution for 2-3 servers)

Use tools like rsync + inotify or lsyncd to sync file changes across servers in near-real time. This eliminates single points of failure but introduces sync latency and configuration complexity. Not recommended for large-scale SaaS deployments.

Best practice: Update plugins, themes, and WordPress core from a single deployment server, then sync the full codebase to all application nodes. Avoid running updates via wp-admin on random nodes to prevent version mismatches.


Step 5: Session and Cache Consistency

WordPress stores login sessions in local files by default. Random load balancing distribution will cause users to be randomly logged out as they switch between servers.

Recommended Solution: Central Redis for shared sessions + object cache

  1. Deploy a dedicated Redis server (private network access only, with password authentication) and connect all WP servers to the same Redis instance.
  2. Configure shared PHP sessions: Update php.ini on all servers:
   session.save_handler = redis
   session.save_path = "tcp://10.0.4.30:6379?auth=your_redis_password&database=0"
  1. Configure WordPress object cache: Install the Redis Object Cache plugin and add configuration to wp-config.php:
   define( 'WP_REDIS_HOST', '10.0.4.30' );
   define( 'WP_REDIS_PORT', 6379 );
   define( 'WP_REDIS_PASSWORD', 'your_redis_password' );
   define( 'WP_CACHE_KEY_SALT', 'wp_saas_' );
  1. Benefits: This not only solves session persistence, but also drastically reduces database load and improves site performance across all subsites.

IP-hash session persistence is not recommended: it causes uneven load distribution, defeats the purpose of horizontal scaling, and is unreliable for SaaS users with dynamic IP addresses.


III. Verification and Smooth Go-Live

  1. Local testing: Modify your local hosts file to point test subdomains and custom domains to the load balancer IP. Verify:
  • Wildcard subsites load correctly with consistent content
  • Custom domains correctly map to their target subsites
  • Login sessions persist across page refreshes
  • Uploaded media is accessible from all backend servers
  1. Gradual traffic cutover: Lower your DNS TTL to 300 seconds first, then shift traffic to the load balancer incrementally while monitoring error logs.
  2. Full launch: After 24 hours of error-free operation, complete the full cutover. Your original server can be retired or retained as an additional cluster node.

IV. Post-Launch Scaling and Optimization

  1. Horizontal scaling: To add more servers in the future, you only need 3 steps: provision the server environment → mount shared storage → add the node to the load balancer pool. No DNS changes required.
  2. Database optimization: As your site count grows, upgrade to a primary-replica database architecture and use the HyperDB plugin to implement read/write splitting, offloading read queries to replica nodes.
  3. Static asset CDN: Route all static assets through a CDN (Cloudflare, AWS CloudFront) to further reduce load on application servers and improve global performance.
  4. Health checks: Configure HTTP health checks on your load balancer (e.g. probing /wp-includes/images/blank.gif) to automatically remove unhealthy servers from the pool for high availability.

V. Key Considerations

  1. Domain mapping compatibility: This architecture works seamlessly with both native WordPress 4.5+ domain mapping (changing the site address in wp-admin) and the legacy MU Domain Mapping plugin. Site resolution relies entirely on the Host header, and is unaffected by server count.
  2. Cookie configuration: Keep your existing COOKIE_DOMAIN and authentication key constants unchanged to preserve cross-subsite login functionality.
  3. Security isolation: Restrict database, Redis, and NFS access to your private network only — never expose these services to the public internet.

For More Information, Please read the post: Part 2: Expanded Load Balancing Deployment Guide | WordPress Multisite Subdomain SaaS Scaling

Leave a Reply

Your email address will not be published. Required fields are marked *