FastCGI cache stores the HTML that PHP-FPM generates, so Nginx can serve repeat requests straight from cache without running PHP or querying the database. For WordPress and other PHP sites, it’s one of the most effective speed-ups available — often turning hundreds of milliseconds into a few.

1. Define the cache zone

In the http {} context, e.g. /etc/nginx/conf.d/fastcgi-cache.conf:

fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPCACHE:100m max_size=2g inactive=60m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_lock on;
  • keys_zone=PHPCACHE:100m — shared memory for keys (1 MB ≈ 8,000 keys).
  • max_size=2g — disk limit for cached pages.
  • inactive=60m — entries not requested for 60 minutes are removed.
  • use_stale — serve old content if PHP errors or while a fresh copy is generated.
  • cache_lock — only one request regenerates a missing entry; others wait (avoids stampedes).
sudo mkdir -p /var/cache/nginx/fastcgi

2. Decide what not to cache

Caching personalised pages for everyone is the classic mistake. In the server block:

set $skip_cache 0;

# POST requests and URLs with query strings
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }

# Admin, login, feeds, sitemaps, WooCommerce pages
if ($request_uri ~* "/wp-admin/|/wp-login.php|/xmlrpc.php|wp-.*\.php|/feed/|sitemap(_index)?\.xml|/cart/|/checkout/|/my-account/") {
    set $skip_cache 1;
}

# Logged-in users, recent commenters, carts
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in|woocommerce_items_in_cart|woocommerce_cart_hash") {
    set $skip_cache 1;
}

(if with set is one of the safe uses of if in Nginx.) Adapt the patterns for other applications: any session or cart cookie should bypass the cache.

3. Enable caching in the PHP location

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_cache PHPCACHE;
    fastcgi_cache_valid 200 301 302 60m;
    fastcgi_cache_valid 404 1m;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    add_header X-FastCGI-Cache $upstream_cache_status;
}
  • fastcgi_cache_bypass — don’t serve from cache for these requests.
  • fastcgi_no_cache — don’t store their responses.
  • X-FastCGI-Cache shows HIT, MISS, BYPASS, EXPIRED, STALE — useful for testing.
sudo nginx -t && sudo systemctl reload nginx

4. Test

curl -sI https://example.com/ | grep -i x-fastcgi-cache    # MISS
curl -sI https://example.com/ | grep -i x-fastcgi-cache    # HIT
curl -sI https://example.com/wp-admin/ | grep -i x-fastcgi-cache   # BYPASS

Log in to WordPress in a browser and check that you see your admin bar (pages bypass the cache for logged-in users).

5. Purging

Cached pages stay until they expire. When you publish or edit content, purge:

  • Clear everything: sudo rm -rf /var/cache/nginx/fastcgi/* (safe; Nginx rebuilds it).
  • Automatic purging from WordPress: the Nginx Helper plugin can delete cache files when posts change (“Delete local server cache files” mode, pointing at your cache path). The web server user needs write access to the cache directory for that.
  • Per-URL purge via the third-party ngx_cache_purge module (packaged on some systems).

Short lifetimes reduce how stale pages can get if purging misses something.

6. Microcaching for dynamic apps

Even a 1-second cache can absorb traffic spikes: if 500 requests per second hit the same page, PHP renders it once per second instead of 500 times.

fastcgi_cache_valid 200 1s;
fastcgi_cache_use_stale updating;

Great for news sites, forums and APIs where content changes often but not every millisecond.

Things to watch

  • Set-Cookie on public pages — some plugins set cookies for every visitor; Nginx won’t cache responses with Set-Cookie unless told to ignore it (fastcgi_ignore_headers Set-Cookie — only if you’re sure the cookie is harmless).
  • Mobile vs desktop themes rendering different HTML — add a device marker to the cache key, or use responsive design.
  • Disk space — set max_size.
  • Debugging — remember to check X-FastCGI-Cache when “my change doesn’t show up”.

FastCGI cache vs plugins vs Varnish

  • Caching plugins (WP Rocket, LiteSpeed Cache, W3 Total Cache) — easier, but PHP still starts for each request unless they write static files Nginx serves directly.
  • FastCGI cache — PHP never runs for cached pages; very fast, server-level.
  • Varnish — more powerful rules and invalidation, an extra component to run.

See also WordPress cache plugins compared and WordPress performance optimization.

  • Nginx
  • Caching
  • WordPress