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.
- Allow users to access the WordPress site at
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.