NGINX Caching Tutorial for WordPress

In this tutorial we will show you how to take advantage of Nginx caching to speed up your WordPress web application.

Nginx Caching vs Varnish Caching

Varnish is an HTTP Accelerator software that is often used by DevOps and Sysadmins in optimizations to their web server setups.

What many people don’t know however, is that Nginx comes with its own highly performant default caching mechanism, fastcgi_cache. Whilst the vanilla implementation is considered imperfect, due to some inconsistencies with cache purging, the guys at FRiCKLE Labs have solved this problem with their ngx_cache_purge module.

The result is a caching mechanism every bit as performant as Varnish. But one that requires a less complex web server architecture. This means fewer potential points of failure and a more resilient platform solution.

At RunCloud we have implemented this module inside our Nginx build since day one, and this tutorial will take you through the steps necessary to take advantage of it.

Just so you know, we keep good company in our belief in the Nginx caching solution. For example the CEO at MaxCDN has said that  you don’t need Varnish under Nginx. While the guy behind EasyEngine has spoken at length about this subject, saying that he doesn’t need Varnish since Nginx can do everything.

To be honest, like many other systems, I agree with both of them. RunCloud has chosen to use Nginx Caching over Varnish.

Nginx Helper Plugin

Before setting up our configuration, you need to install the Nginx Helper Plugin from rtCamp. This is the same company that created EasyEngine.

After you have installed the plugin, you need to configure the plugin as follows

FastCGI or Proxy Cache?

Inside RunCloud, we offer two web server stacks for you to choose from. You can choose either Native Nginx or an Nginx + Apache2 Hybrid. If you are using the Native Nginx stack, you need to use FastCGI cache. And if you are using the hybrid setup, you need to use the proxy cache. Got it?

Cache Zone Setup (For both Native Nginx and Nginx + Apache2 Hybrid)

Open /etc/nginx-rc/main-extra.conf with your favorite text editor, and paste the config below.

fastcgi_cache_path /var/run/nginx-fastcgi-cache levels=1:2 keys_zone=FASTCGICACHE:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;

proxy_cache_path /var/run/nginx-proxy-cache levels=1:2 keys_zone=PROXYCACHE:100m inactive=60m;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_use_stale error timeout invalid_header http_500;
proxy_ignore_headers Cache-Control Expires Set-Cookie;

You may be asking, “why do I need to add both fastcgi and proxy cache?”

Well, this is to give you the option to deploy web applications with either an Native Nginx or Nginx + Apache2 Hybrid stack, without needing to return to edit the configuration. Why not do them both now, does that make sense?

Implementation for Native NGINX

If you are using Nginx + Apache2 Hybrid, skip ahead to the implementation for Nginx + Apache2 Hybrid.

Now you need to know your web application’s name. Remember Linux is case sensitive, so ensure your web application’s name is correct. In this tutorial I will be using {WEBAPP} as a web application name placeholder, wherever you see  {WEBAPP} in this tutorial, remember to change it to your web application name.

Now create and edit /etc/nginx-rc/extra.d/{WEBAPP}.location.main.cache.conf. E.g: If your web application’s name is app-jebat, then it will be /etc/nginx-rc/extra.d/app-jebat.location.main.cache.conf.

Inside there, add the config below

set $skip_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $skip_cache 1;
}
if ($query_string != "") {
    set $skip_cache 1;
}

# Don't cache uris containing the following segments
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
    set $skip_cache 1;
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
    set $skip_cache 1;
}

location ~ /purge(/.*) {
    fastcgi_cache_purge FASTCGICACHE "$scheme$request_method$host$1";
}

Now create and edit /etc/nginx-rc/extra.d/{WEBAPP}.location.proxy.cache.conf. Inside there, add the following config.

fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache FASTCGICACHE;
fastcgi_cache_valid 60m;
add_header X-RunCloud-Cache $upstream_cache_status;

Once you have have done that, make sure that your Nginx config files are syntax error free by issuing the following command.

# nginx-rc -t

You should receive a message saying there are no errors. If the terminal tells you the syntax check has failed, then return to the previous config files and recheck everything.

Finally, once you have passed the syntax check, restart your Nginx server using the following command.

# systemctl restart nginx-rc

Implementation for Nginx + Apache2 Hybrid

Now you need to know your web application name. Make sure your web application name is correct because Linux is case sensitive. Please remember, I will be using {WEBAPP} as a web application name placeholder. Wherever you see {WEBAPP} inside this tutorial, remember to change it to your web application name.

Now create and edit /etc/nginx-rc/extra.d/{WEBAPP}.location.main.cache.conf. E.g: If your web application’s name is app-jebat, it will be /etc/nginx-rc/extra.d/app-jebat.location.main.cache.conf.

Inside there, add the config below

set $skip_cache 0;

# POST requests and urls with a query string should always go to PHP
if ($request_method = POST) {
    set $skip_cache 1;
}
if ($query_string != "") {
    set $skip_cache 1;
}

# Don't cache uris containing the following segments
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap(_index)?.xml") {
    set $skip_cache 1;
}

# Don't use the cache for logged in users or recent commenters
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
    set $skip_cache 1;
}

location ~ /purge(/.*) {
    proxy_cache_purge PROXYCACHE "$scheme$request_method$host$1";
}

After that, create and edit /etc/nginx-rc/extra.d/{WEBAPP}.location.proxy.cache.conf. Inside there, add this config.

proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
proxy_cache PROXYCACHE;
proxy_cache_valid 60m;
add_header X-RunCloud-Cache $upstream_cache_status;

Once you have have done that, make sure that your Nginx config files are syntax error free by issuing the following command.

# nginx-rc -t

You should receive a message saying there are no errors. If the terminal tells you the syntax check has failed, then return to the previous config files and recheck everything.

Finally, once you have passed the syntax check, restart your Nginx server using the following command.

# systemctl restart nginx-rc

Verifying The Setup

To verify your setup is running, you need to inspect the browser response header. If you are using Mac or Linux, luckily Terminal will do the job.

The command you need to run is:

curl -I https://blog.runcloud.io

Change the URL with your own URL. The response that you receive should be similar to.

From this result you can see, the first request was a MISS and then the second was a HIT. Therefore, this demonstrates that Nginx cache is successfully running.

Now you can enjoy both dramatically increased page load speeds and lower server resource usage, great news!

Share This On
Share on facebook
Share on twitter
Share on linkedin
Share on reddit

61 thoughts on “NGINX Caching Tutorial for WordPress”

          1. Most WordPress caching plugins fall over under real load. Run a Load Impact test and then see what happens.

        1. Is this still the case in a hybrid/proxy setup where Nginx is the proxy in front of Apache? (using ServerPilot here)

          It’s never been clear to me whether PHP is even a factor in this setup, since the proxy already serves HTML that was generated by WP Rocket. Would rocket-nginx still improve speeds?

  1. Hi,

    I followed the steps as instructed but the nginx helper plugin isn’t purging the cache. I’m havnig to go in manually and do it. Any idea whats up?

    1. OK I think I might see why… the code for main-extra.config calls for the path to be /var/run/nginx-fastcgi-cache but the cache is actually being stored in /run/nginx-fastcgi-cache (no var). I’m going to try removing the var section and reloading nginx and see what happens

  2. Still have the same problem. Cache won’t clear no matter what plugin I use. I think, and you tell me, the issue may be with the permissions set in the cache folder itself?

    Chip, in the mean time clear it using SSH.

    cd /run/nginx-fastcgi-cache
    rm -rf *

    Cache is cleared and will start to rebuild.

    1. we noticed that purging all cache didn’t work. But it works when adding new post or adding new comment. This is due to the caching module itself doesn’t receive purge all cache command. So, the plugin doing rm -rf behind the scene and that won’t work because inside runcloud, nginx user is not web app user.

      1. Hmmm ok interesting — that makes sense. I assume you don’t have a workaround just yet or you would have included it. I’ll see what I can find out over the weekend.

        In the meantime — would it be best to set the site or app name for each site on the /var/run/nginx-fastcgi-cache line?

        Like /var/run/nginx-fastcgi-cache/app-name

        If I’m running multiple sites/apps on the same server/runcloud?

        1. Having the same issue on an Apache/NGINX hybrid of a site I migrated in. Initial loading of site had screwed up pages and the “purge” function in the nginx helper plugin is not purging the entire cache. I had to manually delete all files/folders in /var/run/nginx-proxy-cache to get the site to load.

          Will a workaround even be possible?

          1. Nothing on my end yet but ignore my question about the path to the caching folder. I see now that fastcgi_cache_key handles that

          2. I’ve been banging my head against this “flush entire cache” issue and would really love some insight from RunCloud.

            I came to the same conclusion as https://github.com/FRiCKLE/ngx_cache_purge/issues/10#issuecomment-24024716 did. The unlinkRecursive function is just recursively deleting folders, which won’t since the web app user doesn’t have permissions. I tried to solve by following the next comment there, which suggested I “add PHP process user to nginx users’ group”. I made a new group, assigned the user my web app is running under and chmod /var/run/nginx-proxy-cache to give that group ownership. I also added define(‘RT_WP_NGINX_HELPER_CACHE_PATH’,’/var/run/nginx-proxy-cache/’); to wp-config.php to point to the correct folder and added that path to my web app’s OPEN_BASEDIR paths. Still no luck.

          3. I think there isn’t any workaround yet. The guys behind the plugin haven’t found one. Perhaps the only solution would be to have this caching functionality built into the RunClouad dashboard per webapp with a purge cache button. The RunCloud platform is only a year old, and incredibly powerful and good value compared to it’s direct competitors, so I am not too fussed about this, fingers crossed they work out a solution in future. And remember, the cache clears perfectly well for things like post and page updates, it’s just the purge all that we need to login to the server to do.

            In any case, what I’ve done is updated the code so that my cache is stored in my User login folder at the same level as my webapps, and mounted that folder in memory (tmpfs – got the instructions from the EasyEngine page about using the NGINX helper plugin). I’ve also adjusted it so that each site has its own separate cache.

            It isn’t a complete solution, but it means I can just SSH into my server and my cache folders are all there immediately, which makes deleting them a bit easier/faster than having to change directory to var/run and then delete the entire cache for all the sites.

      1. This response is a little weird.. you’re part of the Runcloud team, why don’t you ask your team to confirm?

  3. Nginx helper doesn’t flush when requested.
    Have to delete cache manually from terminal.
    Run in terminal to remove cache in folder without deleting the folder :
    rm -rf /var/run/nginx-fastcgi-cache/*

  4. Same problem here, no way to clear the nginx fastcgi cache from WordPress itself, only via ssh.

    This is suboptimal, has anyone found a solution yet?

    Thanks

  5. Any updates when we can get a nginx cache we can manually flush in WordPress? The article isn’t very useful without any option to flush. It would be great to see the Rocket-Nginx integration as well. Is it on a roadmap or similar?

  6. If you are using WP with woocommerce: you’ll need to add the following to ask fastcgi cache not to cache woocommerce related pages etc to
    /etc/nginx-rc/extra.d/{WEBAPP}.location.main.cache.conf

    by using your favourite editor, mine is NANO.

    ************Start here ***************

    Skip cache on WooCommerce pages

    if ($request_uri ~* “/store.|/cart.|/my-account.|/checkout.|/addons.*”) {
    set $skip_cache 1;
    }

    Skip cache for WooCommerce query string

    if ( $arg_add-to-cart != “” ) {
    set $skip_cache 1;
    }

    #true only when the cookie exists
    if ( $cookie_woocommerce_items_in_cart ) {
    set $skip_cache 1;
    }
    **************End here************

    If you are using W3 total cache, add the following

    ************Start here ***************
    location ~ ^/wp-content/cache/minify/(.+.(css|js))$ {
    try_files $uri /wp-content/plugins/w3-total-cache/pub/minify.php?file=$$
    }
    **************End here************

    Ressource : https://gist.github.com/pelmered/616efcde63c17a4dc3cd

  7. Derrick D Threatt

    I just followed this tutorial and its clearing my cache when I update a page. I havent tried to add a new post yet.

    1. It works if you’re updating a page, create a new post or leave a comment. But it won’t clear cache if you try to purge all cache

      1. Would be stellar to have a solution for purging the cache without having to SSH into the server and issue an rm command.

        Any ideas?

    1. Definitely. I keep having this discussion in the WordPress speed up Facebook group.

      Another great thing about NGINX caching is that it will continue to serve your static cached pages, even if your server PHP has gone down, that’s really cool.

  8. Derrick D Threatt

    so now i had to delete a plugin and even if I clear the folder in /run the cache is not cleared. this is insane. any help??

  9. For all of you guys who are still having the cache purging issue, you might want to try adding /var/run/nginx-fastcgi-cache to your OPEN_BASEDIR list in your Web Application settings.

    1. that isn’t going to help because the created cache file is owned by runcloud-www while the web app owner is “runcloud” or other user

  10. Are you ever going to fix this? I know someone mentioned server-side clearing is better than a plugin but not for those of us with clients who need to purge cache themselves and without having to post.

    I ended up having to cancel my account with you guys and I HATED doing it but I needed cache purging to work cause of my clients.

    If it ever gets fixed please post an update as I would love to come back!

    1. Yeah. We got a solution. However, we need to build our own plugin for the solution to works. Will do a new blog post once we have modified the original plugin

      1. Hi,

        Would you mind sharing the solution? Also, What about caching other PHP applications such as Drupal or WHMCS apart for just WordPress. Would you suggest any modified versions of the above script?

  11. Any update on this? I was considering developing something myself for my own clients but decided to check here first and saw this.

  12. Austin Tompkins

    Were all the more recent comments deleted off of this guide?

    I check back regularly on this post for ANY update, even if this plugin has been put on hold, that’s okay, at least it’s an update.

    But now it seems my comment and a lot of other comments were deleted…?

  13. I believe this will help to clear Nginx cache

    New Update from WP Rocket
    3.3.0.1 April 9, 2019
    Third Party Compatibility: Synchronize with NGINX Helper to clear NGINX Cache when WP Rocket cache is cleared (#793)

    1. Has anyone confirmed that the WP Rocket release actually works with NGINX Helper correctly on a RunCloud hybrid deployment?

Leave a Comment

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

You May Also Like