Back to site

Nginx FastCGI cache

Configure Nginx FastCGI cache on a HolyCloud VPS to serve PHP pages from memory and reduce PHP-FPM load.

Nginx FastCGI cache

Nginx FastCGI cache stores PHP responses (HTML) on disk or in memory. On a HolyCloud Performance VPS, it drastically reduces PHP-FPM calls for cacheable public pages (home, articles, idempotent GET APIs).

Prerequisites

  • Nginx + PHP-FPM operational
  • Understanding of Cache-Control headers and session cookies
  • Disk space for the cache zone (/var/cache/nginx/fastcgi)
  • Site where user-specific dynamic content is separated from public content

Global cache zone

In /etc/nginx/nginx.conf, http block:

fastcgi_cache_path /var/cache/nginx/fastcgi
    levels=1:2
    keys_zone=WORDPRESS:100m
    inactive=60m
    max_size=1g
    use_temp_path=off;

Create the directory:

sudo mkdir -p /var/cache/nginx/fastcgi
sudo chown www-data:www-data /var/cache/nginx/fastcgi

vhost configuration

server {
    listen 443 ssl http2;
    server_name exemple.tld;
    root /var/www/exemple;

    set $skip_cache 0;

    # do not cache if session / admin
    if ($request_method = POST) { set $skip_cache 1; }
    if ($query_string != "") { set $skip_cache 1; }
    if ($request_uri ~* "/wp-admin/|/wp-login.php|/panier|/checkout") { set $skip_cache 1; }
    if ($http_cookie ~* "wordpress_logged_in|woocommerce_items_in_cart") { set $skip_cache 1; }

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

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

        fastcgi_cache_key "$scheme$request_method$host$request_uri";
        add_header X-FastCGI-Cache $upstream_cache_status;
    }
}

Understanding X-FastCGI-Cache

| Value | Meaning |

|--------|----------------|

| MISS | PHP executed, then stored |

| HIT | Served from cache |

| BYPASS | Cache skipped ($skip_cache rule) |

| EXPIRED | Entry expired, recalculated |

Test:

curl -sI https://exemple.tld/ | grep X-FastCGI-Cache

Two consecutive requests: MISS then HIT.

Micro-cache for API

For a public read API:

fastcgi_cache_valid 200 10s;
add_header Cache-Control "public, max-age=10";

Reduce inactive if data changes often.

Manual purge

Open-source Nginx has no native HTTP purge without a third-party module. File purge:

sudo find /var/cache/nginx/fastcgi -type f -delete
sudo nginx -s reload

Automate after CMS deployment (CI hook).

Stale cache (option)

If PHP is unavailable, serve an expired version:

fastcgi_cache_use_stale error timeout updating http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;

Useful for resilience; watch for stale content.

Verification and monitoring

sudo nginx -t && sudo systemctl reload nginx
du -sh /var/cache/nginx/fastcgi

Logs:

sudo tail -f /var/log/nginx/access.log | grep -E 'HIT|MISS'

Common pitfalls

  • WooCommerce cart: any cart cookie must bypass the cache.
  • HTTPS mixed: key includes $scheme — OK.
  • Comment moderation: pages with ?replytocom= query → exclude.
  • RAM: keys_zone=100m ≈ 800k keys — adapt to traffic.

Combined with PHP OPcache

FastCGI cache ≠ OPcache: the first avoids running PHP; the second speeds execution. Use both.

Need help?

HolyCloud support: send sanitized nginx -T, curl -I headers, and CMS description if cache never activates (permanent BYPASS).