Nginx FastCGI caching stores the output of PHP-FPM (or any FastCGI backend) responses as files on disk and serves them directly without hitting PHP for subsequent requests. For a WordPress site or PHP application serving the same page to many users, this can reduce PHP execution from 200ms to under 1ms and cut server load by 90%+. Unlike Varnish or Redis-based page caches, Nginx FastCGI cache requires no additional software — the cache is built into Nginx’s ngx_http_fastcgi_module. This guide covers configuring the FastCGI cache zone, integrating it with PHP-FPM, defining cache bypass rules for logged-in users and POST requests, adding cache status headers for debugging, and implementing cache purging.
Prerequisites
- Nginx and PHP-FPM installed on RHEL 9
- PHP-FPM listening on a Unix socket at
/run/php-fpm/www.sock
Step 1 — Configure the FastCGI Cache Zone
Add the cache path definition to the http block in /etc/nginx/nginx.conf:
http {
# Cache zone: 10MB for keys, 2GB max size on disk, 60min inactive cleanup
fastcgi_cache_path /var/cache/nginx/fastcgi_cache
levels=1:2
keys_zone=FASTCGI:10m
max_size=2g
inactive=60m
use_temp_path=off;
# Cache key: unique per scheme+host+URI+query+cookie
fastcgi_cache_key "$scheme$request_method$host$request_uri";
}
# Create the cache directory
mkdir -p /var/cache/nginx/fastcgi_cache
chown nginx:nginx /var/cache/nginx/fastcgi_cache
# Set SELinux context
semanage fcontext -a -t httpd_cache_t '/var/cache/nginx(/.*)?'
restorecon -Rv /var/cache/nginx/
Step 2 — Configure Cache Bypass Rules
Certain requests must always bypass the cache: POST requests, admin pages, and requests from logged-in users:
server {
listen 80;
server_name example.com;
# Variables to control cache bypass
set $skip_cache 0;
# Don't cache POST requests
if ($request_method = POST) { set $skip_cache 1; }
# Don't cache URIs with query strings (search pages etc.)
if ($query_string != "") { set $skip_cache 1; }
# Don't cache WordPress admin and login pages
if ($request_uri ~* "(/wp-admin/|/wp-login.php|/xmlrpc.php)") {
set $skip_cache 1;
}
# Don't cache for logged-in WordPress users
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") {
set $skip_cache 1;
}
Step 3 — Apply Cache to the PHP-FPM Location
root /var/www/example.com;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ .php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Cache settings
fastcgi_cache FASTCGI;
fastcgi_cache_valid 200 301 302 60m; # Cache these status codes for 60 min
fastcgi_cache_valid 404 1m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on; # Serve stale while refreshing
fastcgi_cache_lock on; # Prevent cache stampede
# Expose cache status in response header for debugging
add_header X-FastCGI-Cache $upstream_cache_status;
}
}
nginx -t && systemctl reload nginx
Step 4 — Verify Cache Status
# First request — MISS (not yet cached)
curl -I http://example.com/ | grep X-FastCGI-Cache
# Second request — HIT (served from cache)
curl -I http://example.com/ | grep X-FastCGI-Cache
# Possible values: HIT, MISS, BYPASS, EXPIRED, STALE, UPDATING
Step 5 — Implement Cache Purging
# Install the cache purge module (if available)
dnf install -y nginx-mod-http-cache-purge
# Add to server block — allow purge only from localhost
location ~ /purge(/.*) {
fastcgi_cache_purge FASTCGI "$scheme$request_method$host$1";
allow 127.0.0.1;
deny all;
}
# Purge a specific page
curl -X PURGE http://localhost/some-page/
Step 6 — Monitor Cache Performance
# Check cache disk usage
du -sh /var/cache/nginx/fastcgi_cache/
# View cache hit rate from access logs
grep 'X-FastCGI-Cache: HIT' /var/log/nginx/access.log | wc -l
grep 'X-FastCGI-Cache: MISS' /var/log/nginx/access.log | wc -l
Conclusion
Nginx FastCGI caching on RHEL 9 eliminates redundant PHP executions for repeated page requests, dramatically reducing server load and TTFB (time to first byte). The bypass rules ensure dynamic content (shopping carts, admin, logged-in users) is never served stale. The X-FastCGI-Cache header makes debugging cache behaviour straightforward.
Next steps: How to Set Up Varnish Cache as a Reverse Proxy on RHEL 9, How to Configure HAProxy for HTTP and TCP Load Balancing on RHEL 9, and How to Set Up a LEMP Stack on RHEL 9.