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-Controlheaders 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
| -------- | ---------------- |
|---|---|
MISS |
PHP executed, then stored |
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).