Apache Reverse Proxy – Send /blog Requests to Internal WordPress Server

Apache2dockerreverse-proxyWordpress

I have a website written in react, and now I wanted to add a blog section to the site. The blog is going to be based on wordpress.

The react app runs in a docker container, and I use the wordpress docker container to run the wordpress blog.

In order to access the website, I use another container running apache and acting as a reverse proxy.

Inside the httpd.conf file for the apache container, I have the following section:

<VirtualHost *:80>
    <Location "/">
        ProxyPreserveHost On
        ProxyPass "${REACT_SERVER}/"
        ProxyPassReverse "${REACT_SERVER}/"
    </Location>

    <Location /blog>
        ProxyPreserveHost On
        ProxyPass "${BLOG_SERVER}/"
        ProxyPassReverse "${BLOG_SERVER}/"
        ProxyPassReverseCookiePath  "/"  "/blog"
    </Location>

    # more config for handling websockets
</VirtualHost>

The variables REACT_SERVER and BLOG_SERVER come from the environment.

The problem I'm having is that when I try to access the blog, apache successfully redirects my request to the internal wordpress site, but when wordpress does its own redirect, it uses the same host as apache, but the path does not start with /blog, so my react app tries to handle the request, but eventually gives up and does its own redirect to the home page.

Here is an example using curl:

➜ curl -v http://localhost:3005/blog/
*   Trying 127.0.0.1:3005...
* Connected to localhost (127.0.0.1) port 3005 (#0)
> GET /blog/ HTTP/1.1
> Host: localhost:3005
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 302 Found
< Date: Fri, 20 Aug 2021 16:27:32 GMT
< Server: Apache/2.4.48 (Debian)
< X-Powered-By: PHP/7.4.22
< Expires: Wed, 11 Jan 1984 05:00:00 GMT
< Cache-Control: no-cache, must-revalidate, max-age=0
< X-Redirect-By: WordPress
< Location: http://localhost:3005/wp-admin/install.php
< Content-Length: 0
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host localhost left intact

As you can see, after the X-Redirected-By section, the Location starts with /wp-admin instead of /blog/wp-admin.

From the docs on ProxyPassReverse:

For example, suppose the local server has address http://example.com/;
then

ProxyPass         "/mirror/foo/" "http://backend.example.com/"
ProxyPassReverse  "/mirror/foo/" "http://backend.example.com/"
ProxyPassReverseCookieDomain  "backend.example.com" "public.example.com"
ProxyPassReverseCookiePath  "/"  "/mirror/foo/"

will not only cause a local request for the
http://example.com/mirror/foo/bar to be internally converted into a
proxy request to http://backend.example.com/bar (the functionality
which ProxyPass provides here). It also takes care of redirects which
the server backend.example.com sends when redirecting
http://backend.example.com/bar to http://backend.example.com/quux .
Apache httpd adjusts this to http://example.com/mirror/foo/quux before
forwarding the HTTP redirect response to the client. Note that the
hostname used for constructing the URL is chosen in respect to the
setting of the UseCanonicalName directive.

and it seems that this is all that's required for this to work, but it still doesn't.

And if you are wondering, yes I have tried the plain (without the Location directive):

ProxyPass "/blog/" "${BLOG_SERVER}/"
ProxyPassReverse "/blog/" "${BLOG_SERVER}/"
ProxyPassReverseCookiePath  "/"  "/blog"

# etc...

And I also get the same results.

What am I missing?

Best Answer

This issue looks like it's more of a Wordpress thing than a misconfiguration. You need to tell wordpress that it's living inside a subdirectory, because right now the default wordpress .htaccess file is redirecting you to http://localhost:3005/wp-admin/install.php , because it's not aware it's located in a directory called blog.

Option 1. One way to try and solve this is tell wordpress that it has a new base url in the wp-config.php file

define('WP_HOME','http://example.com/blog');
define('WP_SITEURL','http://example.com/blog');

Option 2. Another way to try and handle this, is to edit wordpress's htaccess file

Your htaccess file in wordpress should look something like this

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /blog/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /blog/index.php [L]

This would be the htaccess that exists inside the wordpress docker container