This tutorial will be showing you how to set up Nginx FastCGI cache to reduce server response time for your WordPress site.
What is Nginx FastCGI Cache?
If you followed my previous tutorials about installing LEMP stack on various Linux distributions, then Nginx is configured to pass PHP request to PHP-FPM because Nginx itself is unable to process PHP code. Let me explain how a LEMP stack website works.
- Web browsers send HTTP requests to Nginx, which tries to fetch the page from file system.
- If Nginx found PHP codes in the page, it passes the request to PHP-FPM to process the PHP codes.
- If necessary, PHP-FPM will query MySQL/MariaDB database to get what it needs.
- PHP-FPM generates a static HTML page, which is then given back to Nginx.
- Finally, Nginx sends the static HTML page to web browser.
Nginx is super fast when it comes to serving static HTML pages. However, PHP is known to be slow, although the latest version PHP7 is much faster than previous versions. And MySQL/MariaDB database is another performance bottleneck of LEMP stack websites.
Instead of passing the dynamic page request to PHP-FPM and let it generate HTML page every time, Nginx can cache the generated HTML page so next time it can send cached pages to web browsers, eliminating PHP and database requests.
- This can greatly improve server response time and reduce load on PHP-FPM and database server.
- It also allows Nginx to serve web pages from cache when the upstream PHP-FPM or database server is down (MySQL/MariaDB is usually the first process to be killed when your Linux server runs out of memory).
FastCGI is the protocol between Nginx and PHP-FPM so the cache is called FastCGI cache.
How to Configure Nginx FastCGI Cache
Step 1: Edit the Nginx Main Configuration File
Edit Nginx main configuration file.
sudo nano /etc/nginx/nginx.conf
In the http context, add the following 2 lines:
fastcgi_cache_path /usr/share/nginx/fastcgi_cache levels=1:2 keys_zone=phpcache:100m max_size=10g inactive=60m use_temp_path=off; fastcgi_cache_key "$scheme$request_method$host$request_uri";
The first directive fastcgi_cache_path
creates a FastCGI cache. This directive is only available under the http
context of an Nginx configuration file.
- The first argument specifies the cache location in the file system (
/usr/share/nginx/fastcgi_cache/
). - The levels parameter sets up a two-level directory hierarchy under
/usr/share/nginx/fastcig_cache/
. Having a large number of files in a single directory can slow down file access, so I recommend a two-level directory for most deployments. If the levels parameter is not included, Nginx puts all files in the same directory. The first directory uses one character in its name. The sub-directory uses two characters in its name. - The 3rd argument specifies the name of the shared memory zone name (phpcache) and its size (100M). This memory zone is for storing cache keys and metadata such as usage times. Having a copy of the keys in memory enables Nginx to quickly determine if a request is a HIT or MISS without having to go to disk, greatly speed up the check. A 1MB zone can store data for about 8,000 keys, so the 100MB zone can store data for about 800,000 keys.
- max_size sets the upper limit of the size of the cache (10GB in this example). If not specified, the cache can use all remaining disk space. Once cache reaches its maximum size, Nginx cache manager will remove the least recently used files from the cache.
- Data which has not been accessed during the inactive time period (60 minutes) will be purged from the cache by the cache manager, regardless of whether or not it has expired. The default value is 10 minutes. You can also use values like
12h
(12 hours) and7d
(7 days). - Nginx first writes files that are destined for the cache to a temporary storage area (
/var/lib/nginx/fastcgi/
).use_temp_path=off
tells Nginx to write them directly to the final cache directory to avoid unnecessary copying of data between file systems.
The 2nd directive fastcgi_cache_key
defines the key for cache lookup. Nginx will apply a MD5sum hash function on the cache key and uses the hash result as the name of cache files. After entering the two directives in the http context, save and close the file.
Step 2: Edit Nginx Server Block
Then open your server block configuration file.
sudo nano /etc/nginx/conf.d/your-domain.conf
Scroll down to the location ~ \.php$ section. Add the following lines in this section.
fastcgi_cache phpcache; fastcgi_cache_valid 200 301 302 60m; fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503; fastcgi_cache_min_uses 1; fastcgi_cache_lock on; add_header X-FastCGI-Cache $upstream_cache_status;
- The
fastcgi_cache
directive enables caching, using the memory zone previously created byfastcgi_cache_path
directive. - The
fastcgi_cache_valid
sets the cache time depending on the HTTP status code. In the example above, responses with status code 200, 301, 302 will be cached for 60 minutes. You can also use time period like12h
(12 hours) and7d
(7 days). - Nginx can deliver stale content from its cache when it can’t get updated content from the upstream PHP-FPM server. For example, when MySQL/MariaDB database server is down. Rather than relay the error to clients, Nginx can deliver the stale version of the file from its cache. To enable this functionality, we added the
fastcgi_cache_use_stale
directory. fastcgi_cache_min_uses
sets the number of times an item must be requested by clients before Nginx caches it. Default value is 1.- With
fastcgi_cache_lock
enabled, if multiple clients request a file that is not current in the cache, only the first of those requests is allowed through to the upstream PHP-FPM server. The remaining requests wait for that request to be satisified and then pull the file form the cache. Withoutfastcgi_cache_lock
enabled, all requests go straight to the upstream PHP-FPM server. - The 3rd line adds the X-FastCGI-Cache header in HTTP response. It can be used to validate whether the request has been served from the FastCGI cache or not.
Now save and close the server block configuration file. Then test your Nginx configuration.
sudo nginx -t
If the test is successful, reload Nginx.
sudo service nginx reload
or
sudo systemctl reload nginx
The cache manager now starts and the cache directory (/usr/share/nginx/fastcgi_cache
) will be created automatically.
Testing Nginx FastCGI Cache
Reload your site’s home page for a few times. Then use curl to fetch the http response header.
curl -I http://www.your-domain.com
Like this:
Take a look at the X-FastCGI-Cache header. HIT
indicates the response was served from cache.
Stuff that Should not be Cached
Login session, user cookie, POST request, query string, WordPress back-end, site map, feeds and comment author should not be cached. To disable caching for the above items, edit your server block configuration file. Paste the following code into the server
context, above the location ~ \.php$
line.
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/*|/tag/.*/feed/*|index.php|/.*sitemap.*\.(xml|xsl)") { 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; }
Sometimes I want to test the upstream (PHP-FPM and MariaDB) response time, so I also add the following lines to tell Nginx to bypass the FastCGI cache for my own IP addresses.
if ($remote_addr ~* "12.34.56.78|12.34.56.79") { set $skip_cache 1; }
The tilde symbol (~
) tells Nginx that what follows is a regular expression (regex). The star symbol *
makes the regex case-insensitve. The vertical bar |
is for alternation of several values. If the value of $remote_addr variable matches any IP address in the regex, then set the value of $skip_cache to 1.
You can also skip the cache for a local network like below, which will add 10.10.10.0/24
network to the skip list.
if ($remote_addr ~* "12.34.56.78|12.34.56.79|10.10.10..*") { set $skip_cache 1; }
Note that if you are using the Google XML sitemap plugin in your WordPress site, then you can probably see the following rewrite rules in your Nginx configuration.
rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml$ "/index.php?xml_sitemap=params=$2" last; rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.xml\.gz$ "/index.php?xml_sitemap=params=$2;zip=true" last; rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html$ "/index.php?xml_sitemap=params=$2;html=true" last; rewrite ^/sitemap(-+([a-zA-Z0-9_-]+))?\.html.gz$ "/index.php?xml_sitemap=params=$2;html=true;zip=true" last;
These rewrite rules should be put below the skip cache rules. If the rewrite rules are above the skip cache rules, then your sitemap will always be cached. Similarly, if you use the Yoast SEO plugin to generate sitemap, then you also need to move the Yoast rewrite rules below the skip cache rules.
Now in the location ~ \.php$
section, paste the following directives.
fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache;
The first directive tells Nginx to send request to upstream PHP-FPM server, instead of trying to find files in the cache. The second directive tells Nginx not to cache the response. Save the file and reload Nginx.
sudo systemctl reload nginx
or
sudo service nginx reload
How to Automatically Purge Cache with WordPress
First, you need to install and activate the Nginx Helper plugin on your WordPress site. Then go to WordPress Settings
-> Nginx helper
and tick on the Enable Purge
box. The default purging conditions are fine for most WordPress blogs. You can also enable logging to check if purging is working correctly.
Click the Save all changes button.
Then you need to install the http-cache-purge
module on your Linux server. Run the following command to install it on Ubuntu 18.04 and above. During the installation process, a configuration file will be put under /etc/nginx/modules-enabled/
directory to enable this module.
sudo apt install libnginx-mod-http-cache-purge
You can also install the nginx-extras
package to enable this module, but this package also enables many other modules. To keep my Nginx as lightweight as possible, I don’t install the nginx-extras
package.
Next, open your Nginx server block configuration file. Add the following lines in the server {...}
context.
location ~ /purge(/.*) { fastcgi_cache_purge phpcache "$scheme$request_method$host$1"; }
Save and close the file. Then test Nginx configurations.
sudo nginx -t
If the test is successful, reload Nginx.
sudo systemctl reload nginx
Now you can modify one of your posts in WordPress to see if the cache will be automatically purged.
Nginx Cache Sniper Plugin
If the Nginx helper plugin doesn’t work, you can also use the Nginx Cache Sniper plugin on your WordPress site. Install the plugin, then go to WordPress Dashboard
-> Tools
-> Nginx Cache Sniper
to configure it.
How to Preload Cache
You can generate page cache before visitors come to your website. Nginx FastCGI cache doesn’t provide a way to preload cache. However, you can use the wget
tool on another box to download the entire website, which will force Nginx to produce a cache for every web page.
wget -m -p -E -k https://www.yourdomain.com/
Make sure the IP address of the other box isn’t on the bypass list. Wget will create a www.yourdomain.com
directory and store the content there.
Sometimes I have to purge the Nginx FastCGI cache and let it regenerate, but I don’t want to manually type the wget
command every time. So I create a Cronjob to automate this task.
crontab -e
Put the following line at the end of the crontab file. /tmp/ramdisk/
is a RAM disk I created to store the files, so the content will be written to RAM and my SSD won’t wear out quickly.
@hourly rm /tmp/ramdisk/www.yourdomain.com/ -r && wget -m -p -E -k -P /tmp/ramdisk/ https://www.yourdomain.com/
Save and close the file. (I know this isn’t a very nice way to solve the problem, but it gets the job done.)
Next Step
I hope this article helped you set up Nginx FastCGI Cache with WordPress. You may also want to use Nginx Amplify to monitor LEMP stack performance.
And if you care about website security, you can set up the ModSecurity web application firewall to protect your WordPress site from hacking.
You may also want to use the Nginx PageSpeed module to optimize website front-end performance.
As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care