Running WordPress behind an nginx reverse proxy on a different domain

WordPress’ security track record speaks for itself.  That being the case, my general goal is to run php apps under unique UID’s, even if they reside on the same website and need to fall within the URI structure of that website.  This often involves the same apps running on the same server, but separated via web server software config so that their PHP configuration can force a specific username per “site”.  This is a non-issue for most applications, but hey, not for WordPress, because it makes the really stupid fucking assumption that you want it to detect what it thinks is the proper hostname for generated URL’s, and stick that in the content being output instead of even its own site URL setting.  This has even been pointed out and was vetoed for repair, because why would anyone ever want to do that.

So, here’s the various steps I needed to get this all working.  First, the pre-requisites:

  • nginx install, acting as the front end for “domain.com” and reverse proxying to separate websites where various applications can be found
  • Some additional server configuration allowing for a website named “blog.local”, with SSL, its own unique PHP config where apps on blog.local run under a different UID, and in a different path than other apps on the same server so there’s no sharing of directories
  • We’re going to make the assumption that all WordPress-bound traffic falls under /blog

Next, we need the nginx reverse proxy configuration:

rewrite ^/blog$ https://www.domain.com/blog/ permanent;
location ^~ /blog/ {
   proxy_pass https://blog.local/;

   # Do not validate SSL
   proxy_ssl_verify off;
   proxy_set_header Host blog.local;
   proxy_redirect default;
   break;
}
# The below is in here for example purposes; it is a greedier match,
# and will take precedent over the above rewrites, so if you have any
# location directives like the below, they must come AFTER the more
# specific patterns.
#
# location ~ \.js$ {
#  add_header Access-Control-Allow-Origin "*";
#}

The above code does a number of things:

  • If the request is for just /blog it redirects to /blog/.  The reason for this is because without that, the pattern match would match anything starting with /blog, even if not desired, such as /blogpage.html, instead of explicitly /blog/<whatever>
  • The WordPress site is running a self signed cert, so we’re not going to validate it.
  • We’re setting the host to request, blog.local
  • Even if blog.local resolves in your DNS, please create a hosts file entry for it so you aren’t doing dns lookups constantly.
  • Redirects issued by the remote content will be rewritten to the domain nginx is serving (i.e. default)

Before we go further, see point 4 above about the hosts file.  You should add hosts file entries for the blog.local domain, don’t rely on DNS unless you have a reason to want to:

192.0.2.1 blog.local www.blog.local
2001:db8::1 blog.local www.blog.local

So at this point, requests to domain.com are answered by nginx natively, and requests for domain.com/blog are proxied to wherever you pointed the proxy_pass directive.  In my case, the same local server listens for both, just with a different virtual host config, different SSL, different php config, etc.  It could have just as easily been a remote server, different port, different port where apache is listening, etc.  None of that matters except that the next server talked to, even if one in the same, knows how to serve blog.local content.

Time to install WordPress.  Extract the files in the blog.local content directory, and we’re going to need to make three changes:

  • The wp-config.php file is capable of overriding the site URL and ‘home’ settings that would normally be detected as blog.local by the installer.  So add these to the file:

define( 'WP_HOME', 'https://domain.com/blog' );
define( 'WP_SITEURL', 'https://domain.com/blog' );

It’s kind of funny that this method of override is even supported given other parts of the code don’t use the home/siteurl settings, nor are they influenced by hard-coding it.  Inconsistency for no good reason.

  • In the same wp-config.php file, you’ll probably need this next line of code added if you installed the WordPress files in the root of the blog.local site:
$_SERVER['REQUEST_URI'] = str_replace("/wp-admin/", "/blog/wp-admin/", $_SERVER['REQUEST_URI']);

July 22 2020 update

The “older version” changes below should no longer be needed.  Just place this code into your wp-config.php file instead, replacing DOMAIN with your real domain:

$_SERVER['HTTP_HOST'] = 'DOMAIN';

Older version:

In two different places of the file wp-admin/includes/class-wp-list-table.php you will need to find and replace the following:

$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );

and replace with:

$current_url = $_SERVER['REQUEST_URI'];

Those changes will of course require re-application after any WordPress updates which replace that file.

Credit to e1ven for finding and reporting this issue nearly a decade ago, even though the WP devs decided to leave things broken:  https://core.trac.wordpress.org/ticket/20562

3 Replies to “Running WordPress behind an nginx reverse proxy on a different domain”

  1. Vinny

    This article is nothing short of brilliant. I came for the WP reverse proxy thing, quickly found the answers I needed, then just re-read the whole thing over because, hey, it’s well-written and entertaining. Your Mom should definitely be proud. Thanks!

    Reply

Leave a Reply to Your Mom Cancel reply

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