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.