How to set up a reverse proxy server for Nginx, Apache and WordPress

Apache is not known for its speed. On the contrary, Apache has garnered a reputation for being quite bloated and performs well under high traffic. However, Apache is still the most popular web server worldwide and is used by many hosting companies due to its familiarity and htaccess. If you still love Apache for some reason and want to speed up your WordPress site, you can put an Nginx reverse proxy caching solution in front of Apache to give users a faster option.

Nginx works as a reverse proxy cache before Apache. Nginx listens on port 80 and Apache listens on port 8080. Nginx will serve any content, it can cache, while all other requests are routed to Apache to handle PHP with MySQL or MariaDB.

Note: This guide won’t work perfect for WooCommerce, a new guide will be posted for Nginx, which works for WooCommerce.

General installation information

Apache configuration instructions for Nginx reverse proxy

Apache Open Ports File

sudo nano /etc/apache2/ports.conf

Change port to 8080

Listen 8080

Open Apache virtual host

nano /etc/apache2/sites-available/wordpress.conf

Change port 8080 in virtual host

<VirtualHost *:8080>

Press Ctrl + X, Y and Enter to save

You will need to change all your Apache virtual hoststo listen on port 8080.

Apache will be resumed after it is installed and configured to avoid any Nginx downtime.

Installing Nginx

Install Nginx and Nginx-extras to get the module ngx_cache_purgewhich will make it easier to manage nginx proxy cache.

sudo apt-get install nginx nginx-extras -y

Creating nginx configuration

sudo nano /etc/nginx/sites-available/reverse

Paste the Nginx config, we need a buffer at the top to prevent this error ( source )

upstream sent too big header while reading response header from upstream errors with buffers

Here is the actual Nginx proxy cache configuration, please note that it is not WooCommerce optimized.

#fix 504 gateway timeouts, can go in nginx.conf
proxy_connect_timeout       600;
proxy_send_timeout          600;
proxy_read_timeout          600;
send_timeout                600;
#set the location of the cached files, zone, name, size (1000 MB) and how long to cache for 600 minutes
proxy_cache_path  /var/run/proxy_cache levels=1:2 keys_zone=WORDPRESS-PROXY:10m max_size=1000m inactive=600m;
proxy_cache_key $scheme$host$request_uri;
#prevent header too large errors
proxy_buffers 256 16k;
proxy_buffer_size 32k;
#httpoxy exploit protection
proxy_set_header Proxy "";

server {
listen          80 default;
access_log /var/log/nginx/proxy-access.log;
error_log /var/log/nginx/proxy-error.log;
add_header X-Cache $upstream_cache_status;

set $do_not_cache 0;
set $bypass 0;

#security for bypass
if ($remote_addr ~ "^(127.0.0.1|Web.Server.IP)$") {
        set $bypass $http_secret_header;
    }

if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
            set $do_not_cache 1;
        }

location / {
	proxy_set_header Host $host;
        proxy_redirect off;
        proxy_cache WORDPRESS-PROXY;
        proxy_cache_revalidate on;
        proxy_ignore_headers  Expires Cache-Control;
        proxy_cache_use_stale  error timeout invalid_header updating http_500 http_502 http_503 http_504;
        proxy_cache_bypass $bypass $do_not_cache;
        proxy_no_cache $do_not_cache;
        proxy_cache_valid 200 301 302 500m;
        proxy_cache_valid 404 1m;
        #can rename PURGE to whatever you want, should restrict it to backend server requests for security
	proxy_cache_purge PURGE from 127.0.0.1 Web.Server.IP;
        proxy_pass  http://127.0.0.1:8080;
        }

location ~ /purge(/.*) {
        allow 127.0.0.1;
        allow Web.Server.IP;
        deny all;
        proxy_cache_purge WORDPRESS-PROXY $scheme$host$1;
        }
}

Press Ctrl + X, Y and Enter

Create symlinks for the Nginx reverse proxy cache for the WordPress virtual host to enable it when we restart Nginx

sudo ln -s /etc/nginx/sites-available/reverse /etc/nginx/sites-enabled/reverse

Unlink Nginx default virtual host

unlink /etc/nginx/sites-enabled/default

Restart Apache and Nginx

sudo service apache2 restart
sudo service nginx restart

Testing Nginx Reverse Proxy Cache

We can use curl to check that the Nginx reverse proxy cache is running on our WordPress site.

sudo apt-get install curl -y

Now Curl is installed, we can start testing the Nginx reverse proxy in front of Apache.

Use SSH on the web server to run the cURL command. This will check that your home page is cached by the reverse proxy and the -I flag ensures that we get the response headers back from the reverse proxy

curl -I https://andreyex.ru/

The key here is the X-Cache status

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:28:26 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Cache: HIT

If the home page is not cached, you will get X-Cache in the response

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:28:26 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Cache: MISS

Sometimes you may need to cURL the same URL twice to get a HIT response.

Nginx Cache Store

If you look in the proxy_cache_path folder, you will see a bunch of random letters and numbers resolved at level = 1: 2. This may seem strange, since Nginx natively stores the cache as md5 hashes of the url based on proxy_cache_key. we use $ scheme $ host $ uri where

  • $ scheme = http
  • $ host = domain
  • $ request_uri = URL

So for this page https://andreyex.ru/kontakty

  • $ scheme is https
  • $ host is andreyex.ru
  • $ request_uri is / kontakty

We can pipe this through the md5 generator on Debian

echo https://andreyex.ru/kontakty | md5sum

It generates this md5 amount

c301d2e9d39fa7434a56322a09dbab17

Which Nginx uses to create the folder structure based on proxy_cache_path = 1: 2 levels

c301d2e9d39fa7434a56322a09dbab17

From levels = 1: 2, 1 becomes the top-level folder and 2 becomes its subdirectory, with the original md5 hash file name

/var/run/proxy-cache/7/b1/c301d2e9d39fa7434a56322a09dbab17

Knowing how the Nginx cache works means we can selectively remove items from the reverse proxy cache.

Flushing and invalidating Nginx reverse proxy cache

Thanks to the module ngx_cache_purgewhich is included in the Nginx-extras module, we have several ways to force selective cache invalidation. Our goal is to have the entire site cached using this plugin so that your WordPress site is fully cached and always fast. Whenever we update content we want to clear the cache for those posts, pages or categories that change and replace those old elements with fresh new ones right away, so your users get the fastest experience possible.

  • Nginx proxy cache is stored in a folder structure in the / var / run / proxy-cache folder, in which we can selectively delete certain items or delete everything to clear the entire cache
  • BYPASS allows you to force-refresh posts or pages by asking the web server where WordPress is suggested for the new version
    • The updated item will replace the deprecated item in Nginx Reverse Proxy Cache
  • PURGE method, proxy_cache_purge allows non-RFC HTTP methods to clear specific items from the cache
  • URL / purge, the method allows you to attach a URL to the purge location to purge a specific element

Clear all cache in Nginx reverse proxy

If you want to clear the entire cache, you can simply delete the contents of the proxy cache folder manually

rm -R /var/run/proxy-cache/*

You can also remove specific items if you want by generating an MD5 hash of the full URL, or you want to clear and delete a specific folder and subfolder recursively in the proxy_cache_path folder.

If you want to clear your entire cache using regular expressions (also known as wildcards) your only option is to use Nginx plusthat costs money. Nginx Plus engineers know that having a high performance WordPress site means keeping the entire site in cache all the time so large companies will pay for flexible cache management.

Update products in Nginx reverse proxy using BYPASS method

Bypass is by far the best way to update your Nginx reverse proxy cache. With proxy_cache_bypass you will force the new version of the URL from the WordPress web server and replace the old obsolete version with the new fresh version. Your users will never get old content this way, and will never get slow PHP compilation on the fly from your web server (unless they are cacheable).

In the block above, we have implemented proxy_cache_bypass only for requests that come from our web server or the reverse proxy server itself

We have enabled a secret header for incoming requests from the web server and reverse proxy, so we can check using the secret header with Curl from those servers.

curl -I https://andreyex.ru -H "secret-header: true"

You will see this output showing BYPASSinX-Cache header

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:28:26 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Cache: BYPASS

If you try the same Curl command from another server, you will just see the HIT response

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:28:26 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Cache: HIT

Updating Items in Nginx Reverse Proxy Using PURGE Method

The PURGE method is kindly provided by the module ngx_cache_purge in the nginx-extras package.

For this method to work, I found proxy_cache_key, which should have been set to $ scheme $ host $ request_uri, but your experience may vary.

To send a PURGE request, use this syntax, the proxy_cache_purge module will translate the request into the md5 hash of the URL and remove the item from the proxy_cache_path folder of the Nginx reverse proxy virtual host.

curl -X PURGE -I https://andreyex.ru

If the reverse proxy has a file, then you will receive a 200 response, which means successful

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive

If the Nginx reverse proxy server does not have this specific cached URL then you will get a 404

HTTP/1.1 404 Not Found
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

If Nginx reverse proxy detects that PURGE requests are not allowed on your IP address, you will receive a response: 403 Forbidden

HTTP/1.1 403 Forbidden
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

This type of Nginx reverse proxy security is important as it restricts PURGE requests to a whitelist of your trusted servers

Purge Items in Nginx Reverse Proxy Using the / Purge URL Method

The / purge URL method uses a specific URL to call the Nginx proxy_cache_purge method we implemented above.

Curl command is used to clear with url, to clear home page, slash is presented

curl https://andreyex.ru/purge/ -I

You will see this response if the home page is cached to the reverse proxy by Nginx and you successfully clear it

HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 277
Connection: keep-alive

If your Nginx reverse proxy does not have a cached home page in WordPress, you will see a 404 error

HTTP/1.1 404 Not Found
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

If your Nginx reverse proxy does not allow you to access the location / purge, you will receive an error: 403 Forbidden

HTTP/1.1 403 Forbidden
Server: nginx/1.8.1
Date: Fri, 03 Feb 2017 13:38:26 GMT
Content-Type: text/html
Content-Length: 168
Connection: keep-alive

Similar to the PURGE method, we restrict access to the / purge location by whitelisting IP addresses so that attackers cannot overload your WordPress web server.

Sidebar