Configuring Nginx as a Reverse Proxy for Subdirectory WordPress Sites

Configuring Nginx as a Reverse Proxy for Subdirectory WordPress Sites

When setting up Nginx as a reverse proxy for a WordPress site hosted in a subdirectory, it’s essential to configure it properly to handle HTTPS, maintain subdirectory structure, and forward the real client IP to the backend server. This guide will walk you through the steps to achieve these goals in a clear and efficient way.

Scenario Overview

In this setup, we have the following:

  • A Server: Acts as the public-facing server configured with HTTPS and Nginx as the reverse proxy.
  • B Server: Hosts the WordPress site, accessed via A Server’s proxy.
  • Goal:
    • Allow users to access the WordPress site at https://example.com/news.
    • Maintain the subdirectory /news structure.
    • Ensure B Server recognizes real client IP addresses.
    • Avoid redirect loops and improper URL resolution.

Step 1: Configure Nginx on A Server

The Nginx on A Server will receive client requests and forward them to B Server while ensuring the subdirectory structure remains intact.

Nginx Configuration

Edit the Nginx configuration file for your domain (e.g., /etc/nginx/sites-available/example.conf):

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /path/to/certificate.pem;
    ssl_certificate_key /path/to/key.pem;

    location /news/ {
        proxy_pass http://B_Server_IP/news/; # Forward to the WordPress site on B Server
        proxy_set_header Host $host;       # Preserve the original Host header
        proxy_set_header X-Real-IP $remote_addr;  # Forward the client IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # Forward the full chain of client IPs
        proxy_set_header X-Forwarded-Proto $scheme; # Indicate HTTPS or HTTP from the client

        proxy_redirect http://B_Server_IP/news/ /news/; # Correct redirects from B Server
    }

    # Enforce HTTPS for all connections
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Key Notes

  • proxy_pass: Forwards requests to B Server, ensuring that /news remains consistent.
  • proxy_set_header: Ensures that the necessary headers (client IP, protocol, host) are forwarded to B Server.
  • proxy_redirect: Fixes redirect URLs returned by B Server to align with the client-facing URL.

Save the file, then test and reload Nginx:

sudo nginx -t  # Test configuration syntax
sudo systemctl reload nginx  # Reload Nginx to apply changes

Step 2: Configure Nginx on B Server

On B Server, Nginx needs to trust the forwarded headers from A Server and correctly process the subdirectory structure.

Enable Real IP Module

Ensure that Nginx on B Server has the real_ip module enabled. Check with:

nginx -V 2>&1 | grep real_ip

If it’s not enabled, recompile Nginx or use a package manager to install the appropriate version.

Nginx Configuration

Edit the Nginx configuration on B Server:

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Trust requests from A Server
    set_real_ip_from A_Server_IP;  # Allow real IPs from A Server
    real_ip_header X-Forwarded-For;  # Use the X-Forwarded-For header for client IPs

    server {
        listen 80;
        server_name B_Server_Domain;

        root /path/to/wordpress;

        location /news/ {
            # Add necessary WordPress configuration here
        }
    }
}

Key Notes

  • set_real_ip_from: Configures Nginx to trust forwarded headers from A Server.
  • real_ip_header: Specifies the header containing the real client IP.

Restart Nginx after saving:

sudo nginx -t  # Test configuration
sudo systemctl restart nginx  # Restart Nginx

Step 3: Configure WordPress

WordPress needs to recognize HTTPS requests and properly handle the subdirectory.

Update wp-config.php

Edit the WordPress configuration file on B Server:

// Recognize HTTPS forwarded by A Server
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

// Correctly handle client IPs
if (!empty($_SERVER['HTTP_X_REAL_IP'])) {
    $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_REAL_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
    $_SERVER['REMOTE_ADDR'] = trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
}

Set WordPress Site URL

Ensure the WordPress site and home URLs match the subdirectory setup. In the WordPress dashboard (Settings > General):

  • WordPress Address (URL): https://example.com/news
  • Site Address (URL): https://example.com/news

Alternatively, update these values directly in the database:

UPDATE wp_options SET option_value = 'https://example.com/news' WHERE option_name IN ('siteurl', 'home');

Step 4: Testing

Check Real Client IP

Inspect the Nginx logs on B Server to verify that real client IPs are recorded:

sudo tail -f /var/log/nginx/access.log

A correct entry should resemble:

192.168.1.100 - - [07/Jan/2025:14:00:00 +0000] "GET /news/ HTTP/1.1" 200 1234 "-"

Test HTTPS and Subdirectory

Visit https://example.com/news and ensure the site:

  • Loads correctly with the subdirectory structure.
  • Displays HTTPS in the browser.
  • Shows no redirect loops or broken links.

Verify WordPress Logs

Use plugins like WP Security Audit Log to ensure that WordPress correctly logs client IPs and URLs.

Step 5: Optional Optimizations

Enable Caching

Improve performance by enabling caching on A Server using proxy_cache:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m;
proxy_cache_key "$scheme$request_method$host$request_uri";

location /news/ {
    proxy_pass http://B_Server_IP/news/;
    proxy_cache my_cache;
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;
}

Conclusion

By carefully configuring Nginx on both A and B Servers, and updating WordPress to recognize forwarded headers, you can:

  • Host a WordPress site in a subdirectory with HTTPS support.
  • Preserve the subdirectory structure for seamless user experience.
  • Ensure B Server logs the real client IP.

This setup provides a secure, scalable foundation for serving WordPress sites behind an Nginx reverse proxy.