Configuring WordPress on NGINX behind an NGINX reverse proxy
If you’re hosting a WordPress site behind an NGINX reverse proxy, it’s not easy or straightforward to get the whole thing working. WordPress, it seems, is hardly designed to be stuck behind a reverse proxy and it gets fully pissed off if you try. It took me two days to get it working right, meaning scouring lots and lots of forums, and finally I resorted to trial-and-error, which is the worst way to accomplish anything. I’m writing this to hopefully save you some time.
NOTE: I am not an expert. I am an amateur. This information isn’t meant to be authoritative, just a guide to the basics of how I solved this problem for my own purposes.
SYMPTOMS: From the get-go, WordPress wasn’t using CSS or Javascript at all, even for the installer. Everything looked completely text-based. The installer worked, though, but once WordPress was installed and depending on the settings I tried, images were broken, and WordPress URLs would redirect to the server’s internal IP. Also I would often get strange subdirectories inserted into the URLs, especially with the wp-admin backend. Also sometimes I’d get partial-SSL warnings from my browser.
I’m using NGINX as a reverse proxy on one server, then using it to route traffic to a WordPress site on NGINX on another server. This guide will probably work if you’re using Apache to run WordPress, but you’ve got to be using NGINX as reverse proxy.
SO THE ROOT OF ALL THIS is SSL not playing nice with the reverse proxy. WordPress really, really wants to operate through SSL, and although my reverse proxy sends and receives all external traffic via SSL, LAN traffic isn’t encrypted so the webserver sees the connection as insecure. All we really need to do is tell WordPress that the traffic is fine.
Reverse proxy setup
First, we need to forward some header information from the reverse proxy to the web server. At the most basic, assuming you have the standard NGINX “default” file in place, a reverse proxy server block looks like this:
server {
server_name domain1.com;
location / {
proxy_pass http://192.168.1.111/;
}
}
There’s lots more configuration you can put in a server block, but this is all you really need to have a functioning reverse proxy route external requests to domain1.com to a server on the local network at 192.168.1.111.
Now, here’s the bare minimum that’s required in a server block to pass along the SSL headers required for WordPress, along with SSL info that isn’t within the scope of this article:
server {
listen 443 ssl;
server_name domain1.com;
location / {
proxy_pass http://192.168.1.111/;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
ssl_dhparam /path/to/ssl-dhparams.pem
}
After you've made the necessary changes, restart NGINX:
sudo service nginx restart
wp-config.php setup
Now we need to configure WordPress. We’ve got to edit the wp-config.php file, which is in the root of the WordPress file directory, but isn’t created until WordPress is installed. So install WordPress if you haven’t already (visit your web server in a browser and follow the instructions), and you’ll probably have to live without CSS and Javascript, but it still works. Once that’s done, fire up your favorite text editor and edit wp-config.php:
sudo nano wp-config.php
You’ll need to add a few lines to the top of the file:
<?php
if ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
$_SERVER['HTTPS'] = 'on';
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}
/**
* The base configuration for WordPress ...
Once you’ve added the lines, save the file and restart NGINX on the web server:
sudo service nginx restart
WordPress address setup
Now take a look at your site and it should be looking much better. But if you’re like me, you’ll notice that once you start clicking around, the URLs begin redirecting to the internal server IP, which won’t work. This is because WordPress tries to be helpful during installation, and rather than just serve pages normally and allow the reverse proxy to do its thing, it wants to control things and sets the domain to what it guesses it should be. In my case, the domain was set to the internal server IP, not the external domain name, so every time I would load a page, it would force the site back to the IP.
There are two ways to fix this. The first, easiest way is to go to your WordPress site and go to Dashboard => Settings ==> General Settings. There you will find WordPress Address (URL) and Site Address (URL). They’re probably set to your server’s internal IP address. Change the values to your domain name, with the http:// included:
http://domain1.com
But if you do this wrong, or if you couldn’t access the dashboard in the first place, you might be stuck. Thankfully, you can edit these values by adding lines to the wp-config.php file. If you do edit the file rather than do it through the dashboard, your dashboard options will be grayed out since you hardcoded the values. To do this, head back to your text editor and edit wp-config.php by adding these lines:
define( 'WP_SITEURL', 'http://domain1.com');
define( 'WP_HOME', 'http://domain1.com');
Remember to enter your domain name in place of domain1.com. Now, there’s another option here, which is to use the reverse proxy server to set the domain name and tell WordPress to use the root. That way you can change your domain name or point another one to WordPress and it’ll be seamless. If you want to do this, instead of the previous code, insert this code instead:
define( 'WP_SITEURL', '/');
define( 'WP_HOME', '/');
Once you’ve added your chosen lines to wp-config.php, restart NGINX:
sudo service nginx restart
Fixing strange redirects
This last thing may not be happening to you. If your WordPress site is operating as you expect it to, then don’t bother with this, but in my /wp-admin/ section (the Dashboard), WordPress kept trying to insert an extra subdirectory in the URL which would break pages, like this:
https://domain1.com/domain1/wp-admin/blah_blah…
The extra subdirectory was the same as its folder on my server. I could remove the extra subdirectory from the URL and it would load the page, but it was really annoying to do it on every page load in the Dashboard. I am 100% sure I have misconfigured something simple somewhere, but I after lots of searching I couldn’t find it. It’s fixable as-is, though with another line of code added to the wp-config.php file.
$_SERVER['REQUEST_URI'] = str_replace("/domain1/wp-admin/", "/wp-admin/",
$_SERVER['REQUEST_URI']);
This is just a simple redirect. You can edit it for your own purposes. The “str_replace” command takes one URL or section of a URL and replaces it with another. In this case, I’m telling WordPress to remove the extra subdirectory, when it appears with “wp-admin”, and replace it with “wp-admin” only. You can play with this to add or take away things as you need. One day I’ll find the real problem and fix it, but for now this works fine.
So that’s it. Hopefully this was helpful. If I screwed something up, or if this article was helpful, let me know in the comments.