Hello world! (or: How to WordPress behind Reverse Proxy)

Why does it have to be so troublesome?

I have now, knock on wood, finally managed to set up WordPress multisite behind an Nginx Reverse Proxy with working subdomains and corresponding certificates. Previous attempts had resulted in long response times and timeouts, which almost made me give up on the idea. But today, I once again delved into the madness and started with some terminal-fu.

In previous attempts, I had installed the Fastest Cache plugin, which satisfied WordPress’s recommendation to use page and object caching, all within a single plugin. The problem with this is that it adds a lot of redirects to the .htaccess file, and I suspect that this was causing the long response times for pages and the API.

And since there are probably more people who need a setup like this, I thought I’d share my settings.

First and foremost, we need an Nginx configuration for the domain/subdomain that needs to be accessed. This configuration will also provide the pages with their certificates:

server {
server_name chuggybumba.com;

listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/chuggybumba.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chuggybumba.com/privkey.pem;

location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass https://[INTERN IP-ADRESS]:443/;
}
}

server {
if ($host = chuggybumba.com) {
return 301 https://$host$request_uri;
}

server_name chuggybumba.com;
listen 80;
return 404;
}

As you can see, Let’s Encrypt is already configured here. To get started with Let’s Encrypt using this configuration, set up a virtual host that only listens on port 80, and then let CertBot take care of the configuration. Once it’s done, adjust using the settings mentioned above. Note that it’s absolutely important to have a slash ( / ) after the port number in the proxy_pass command!

We’re proxying directly to port 443 on the internal server, as this is to ensure that the WordPress API functions smoothly and doesn’t need to go through multiple redirects.

The next configuration is for the local Apache server (though Nginx could be used here as well, I’m following the WordPress-recommended installation, so Apache it is!)

<VirtualHost *:443>
ServerAdmin admin@chuggybumba.com

ServerName chuggybumba.com
DocumentRoot /var/www/wordpress

SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

<Directory /var/www/wordpress>
Options FollowSymLinks
AllowOverride Limit Options FileInfo
DirectoryIndex index.php
Require all granted
</Directory>

<Directory /var/www/wordpress/wp-content>
Options FollowSymLinks
Require all granted
</Directory>
</VirtualHost>

In the Apache configuration, we don’t need to set up any subdomain virtual hosts, as these are handled by the reverse proxy on the Nginx machine. We also don’t need to use a valid certificate here. Instead, we have installed a snake-oil certificate to allow us to deliver encrypted traffic to the reverse proxy.

After this, you can simply follow WordPress’s own instructions to set up the multisite configuration.

Once everything is up and running, make sure not to use plugins that add their own redirects to the .htaccess file, such as Fastest Cache, as this will only lead to issues. Instead, I recommend installing WP Super Cache for page caching and Memcached for object cache storage.