PERFORMANCE
June 30, 2026

How to Configure Full-Page Caching with Nginx FastCGI

10 min read
Author
CloudStick Team
Security Specialist
Share this article
How to Configure Full-Page Caching with Nginx FastCGI
CloudStick
Performance Guide

What Is FastCGI Cache?

Nginx FastCGI cache stores the complete HTML output of a PHP response on disk, then serves that stored file directly to future visitors — skipping PHP-FPM, MariaDB, and every line of application code entirely. The result is sub-millisecond response times for cached pages, regardless of traffic volume.

Unlike object caching (which speeds up the PHP layer) or browser caching (which only helps repeat visitors on the same device), FastCGI cache operates at the Nginx layer — before your application stack is ever involved. A cold visitor hits a cached page just as fast as a returning one with a primed browser cache.

This technique is sometimes called "full-page caching" or "page cache" and is the same mechanism used by tools like WP Super Cache's Nginx mode or the FastCGI Cache plugin for WordPress. You don't need a plugin to configure it at the server level — in fact, the server-level implementation is more reliable and harder to accidentally disable.

PREREQUISITE

You need a CloudStick-managed server running Ubuntu 22.04 with Nginx (nginx-cs) as the frontend proxy, PHP-FPM (8.1, 8.2, 8.3, or 8.4) configured for your site, and root or sudo SSH access. A working WordPress or PHP site should already be live before proceeding.

How FastCGI Cache Works Under the Hood

When a request arrives, Nginx evaluates a cache key — typically built from the request URI, scheme, and host. If a valid cached file exists for that key on disk, Nginx returns it immediately without touching PHP or your database. If the cache is cold, stale, or explicitly bypassed, the request passes through to PHP-FPM (and in CloudStick's stack, through the Apache backend on port 81), which generates the response. Nginx then saves that response to disk before forwarding it to the client.

Two directives control the cache lifecycle. fastcgi_cache_valid sets how long a cached entry stays fresh — after this window, Nginx re-fetches from PHP even if a cached file exists. fastcgi_cache_bypass tells Nginx when to skip reading from the cache, and fastcgi_no_cache tells Nginx when to skip writing to it.

The cache lives in a directory on your server's filesystem, typically under /var/cache/nginx/fastcgi. Nginx manages eviction automatically based on inactive time and max_size limits. You purge it manually with rm -rf or via the Nginx Cache Purge module if installed.

Configure Nginx for FastCGI Caching

Configuration happens in two places: the global nginx.conf (or a file it includes) where you declare the cache zone, and the per-site server block where you activate caching and define bypass rules.

Start by declaring the shared memory zone and cache path in the http block. On CloudStick servers this block lives in /etc/nginx-cs/nginx.conf or a drop-in under /etc/nginx-cs/conf.d/.

# /etc/nginx-cs/conf.d/fastcgi-cache.conf
# Declare cache zone in the http{} block
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=WORDPRESS:100m
inactive=60m
max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating
http_500 http_503;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;

The keys_zone=WORDPRESS:100m directive allocates 100 MB of shared memory for cache metadata — this can track roughly 800,000 cache keys. The max_size=1g cap limits actual disk usage. Adjust both based on your site's page count and server resources.

Next, create the cache directory and add the per-site server block configuration:

# Create cache directory
mkdir -p /var/cache/nginx/fastcgi
chown www-data:www-data /var/cache/nginx/fastcgi
# In your site server block (location ~ \.php$)
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_valid 301 24h;
fastcgi_cache_valid 404 1m;
# Expose cache status in response headers (for debugging)
add_header X-FastCGI-Cache $upstream_cache_status;

The X-FastCGI-Cache header will return HIT, MISS, BYPASS, or EXPIRED — invaluable for confirming the cache is working before you remove the header in production.

Cache Exclusions: What Must Never Be Cached

Full-page caching breaks logged-in users, checkout flows, and any page that renders user-specific content. The bypass logic is non-negotiable: get it wrong and you'll serve one user's cart or admin session to every visitor.

Add these bypass rules in the server block, before the location ~ \.php$ block:

# Variables controlling cache bypass
set $skip_cache 0;
# POST requests always bypass
if ($request_method = POST) { set $skip_cache 1; }
# Query strings bypass (search results, paginated feeds)
if ($query_string != "") { set $skip_cache 1; }
# WordPress-specific exclusions
if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-login.php|
wp-.*.php|/feed/|index.php|sitemap(_index)?.xml)") {
set $skip_cache 1;
}
# Logged-in users and WooCommerce sessions
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|
wp-postpass|wordpress_no_cache|woocommerce_cart_hash|
woocommerce_items_in_cart") {
set $skip_cache 1;
}
# Apply skip variables in your PHP location block
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;

If you run WooCommerce, also exclude the cart, checkout, and account pages by URI. The cookie-based detection handles most cases, but an explicit URI exclusion is a reliable second line of defense for stores with complex session configurations.

WARNING

Never cache pages that set Set-Cookie headers containing session or authentication data. If Nginx caches a response that includes a session cookie, every subsequent visitor will receive that same cookie value, potentially gaining access to another user's session. Always test your bypass rules by logging in and checking that X-FastCGI-Cache returns BYPASS for all admin and account pages.

Verify and Test the Cache

After reloading Nginx, verification is three steps: confirm the configuration is valid, check the response headers, and measure the actual time difference between a cold and warm request.

# Test Nginx configuration before reloading
nginx-cs -t
# Reload without dropping connections
systemctl reload nginx-cs
# First request — expect MISS
curl -sI https://yourdomain.com/ | grep -i x-fastcgi
# X-FastCGI-Cache: MISS
# Second request — expect HIT
curl -sI https://yourdomain.com/ | grep -i x-fastcgi
# X-FastCGI-Cache: HIT
# Measure response time improvement
curl -o /dev/null -s -w "Time: %{time_total}s\n" https://yourdomain.com/
# Confirm bypass for logged-in sessions
curl -sI --cookie "wordpress_logged_in_abc=1" https://yourdomain.com/ \
| grep -i x-fastcgi
# X-FastCGI-Cache: BYPASS

A well-configured FastCGI cache typically reduces TTFB from 200–800 ms (PHP-generated) to 2–15 ms (cache hit). If you're seeing MISS on every request, check that the cache directory exists, has correct permissions, and that your fastcgi_cache_path directive is in the http context, not nested inside a server block.

You can also inspect the cache directory directly to confirm files are being written: ls -lh /var/cache/nginx/fastcgi/ will show the hashed cache files after the first population pass. Each file corresponds to a unique cache key.

CloudStick Workflow: Fitting FastCGI Cache Into Your Stack

CloudStick's server architecture runs Nginx (nginx-cs) as the public-facing proxy on port 443/80, with Apache (apache2-cs) handling dynamic requests on port 81. PHP-FPM processes are managed through CloudStick's package layer at /CloudStick/Packages/, supporting PHP 8.1 through 8.4. FastCGI cache sits entirely within the Nginx layer, so it intercepts requests before they ever reach Apache or PHP-FPM — which is precisely why the performance gains are so dramatic.

When you need to purge the cache after a WordPress update or content publish, CloudStick's dashboard gives you direct SSH access and a terminal where you can run the purge command without leaving the browser. For WordPress sites, you can also pair the server-level FastCGI cache with a plugin like Nginx Helper, which hooks into WordPress publish events and triggers selective cache purges — so your homepage and category archives refresh the moment you publish, without a full cache wipe.

FastCGI cache and Redis object cache address different bottlenecks and stack perfectly together: FastCGI serves anonymous visitors from disk without touching PHP, while Redis eliminates database round-trips for the requests that do reach PHP — logged-in users, dynamic pages, and WooCommerce sessions.

One important consideration on the CloudStick stack: the Nginx configuration files for individual sites are managed by CloudStick's provisioning system. Before adding custom directives, locate the correct include path for your site's server block — typically under /etc/nginx-cs/sites-enabled/ — and add your cache directives in a custom include rather than editing the generated file directly. CloudStick preserves custom include files across provisioning updates.

For cache invalidation on a schedule rather than on publish, you can use CloudStick's Cron Jobs section in the dashboard to run a purge script at a set interval — useful for sites where content changes on a predictable schedule and you want full control over when the cache refreshes without relying on a WordPress plugin hook.

Next Steps: Layering Your Caching Strategy

FastCGI cache handles the heaviest lift — serving anonymous traffic from disk — but a complete caching strategy covers every layer of the stack. Once FastCGI cache is confirmed working (you're seeing consistent HIT responses on public pages), the next priorities are:

Redis object cache. Install Redis via your system package manager and activate the Redis Object Cache plugin in WordPress. This eliminates repetitive MariaDB queries for logged-in users, WooCommerce sessions, and any page that bypasses FastCGI. With both layers active, your PHP execution time for cache-miss requests drops dramatically because WordPress itself runs faster.

Browser and CDN cache headers. Add Cache-Control headers for static assets (images, fonts, scripts, stylesheets) with long max-age values and content-addressed filenames. These assets should never reach PHP — Nginx serves them directly from disk. Pair this with a CDN if your audience is geographically distributed.

PHP opcode cache. OPcache is enabled by default in PHP-FPM on CloudStick-managed servers, but verify it's tuned correctly: opcache.memory_consumption should be at least 256 MB for a WordPress site with plugins, and opcache.validate_timestamps=0 in production eliminates the filesystem check overhead on every request.

Monitor cache hit rate. A healthy FastCGI cache should achieve a hit rate above 85% for a typical WordPress blog. If you're below that, investigate which pages are generating persistent MISSes — often it's query string parameters from analytics scripts (like utm_source, fbclid) that are being included in the cache key. Strip known tracking parameters from the cache key using a map block or by normalizing the request URI before it reaches the key computation.

The configuration covered in this guide — cache zone declaration, bypass rules, exclusions, and header-based verification — gives you a production-ready FastCGI cache foundation. Every optimization added on top of it compounds: Redis and FastCGI together can reduce the database load by over 95% on high-traffic WordPress sites, letting a single CloudStick-managed VPS comfortably serve traffic that would otherwise require horizontal scaling.

Leave a comment
Full Name
Email Address
Message
Contents