WP Manifestindependent plugin directory
manifest / performance / wordpress-fastcgi-cache-purge

FastCGI Cache Purge

WordPress FastCGI cache purging with automatic and manual controls.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/rawhasan/wordpress-fastcgi-cache-purge/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

WordPress Nginx FastCGI Cache + Safe Purge

Created by ChatGPT for Raw Hasan
Date: 25 October 2026
Purpose: Reusable setup for WordPress on Ubuntu, Nginx, and PHP-FPM, including automatic cache invalidation and a manual WordPress Admin purge button.

Scope: A reusable adaptation of a configuration tested on a live development installation. This public version uses generic paths and requires site-specific configuration and testing. It is not a drop-in one-command installer. Do not commit credentials, private keys, database passwords, production IPs, or real customer data.

Start here: a simple explanation

This is a three-part system. Think of the Nginx cache as a storage room holding ready-made copies of your website pages. When a visitor arrives, Nginx can hand over a saved page instead of asking WordPress to build it again. When you edit a published page, the old copy needs to be removed.

  1. Nginx creates and stores the cached pages in a folder on the server.
  2. A purge helper is a small Linux script that empties only that site's cache folder.
  3. The WordPress plugin asks the helper to run after published content changes or when an administrator clicks Purge Nginx Cache.
Visitor → Nginx → saved page (fast)
WordPress page updated → plugin → purge helper → delete saved pages
Next visitor → Nginx creates fresh saved pages

Where does /usr/local/sbin/purge-example-cache come from?

You create it during installation. It is not included in Ubuntu, Nginx, or WordPress under that name.

This repository includes a file named purge-wordpress-fastcgi-cache. Copy it onto your server, give the copy a site-specific name, and tell it which cache folder to clear:

File supplied in this repository: purge-wordpress-fastcgi-cache
               ↓ copy and rename
Installed file on the server:    /usr/local/sbin/purge-example-cache
               ↓ configured to clear
Cache folder on the server:      /sites/example.com/cache

The folder /usr/local/sbin/ is a normal Linux location for administrator-managed scripts. Do not type the helper path into wp-config.php until you have actually installed that helper.

What is wp-config.php and why edit it?

wp-config.php is WordPress's settings file. It already exists on your website. Do not replace it. We add one line telling the plugin where its helper lives:

define('RH_FASTCGI_PURGE_HELPER', '/usr/local/sbin/purge-example-cache');

In plain English: “WordPress, when you need to clear the page cache, run the script at this location.”

For this guide, open /sites/example.com/public/wp-config.php and insert the line before the line that loads wp-settings.php. You can usually place it just above the /* That's all, stop editing! Happy publishing. */ comment. Keep every other setting unchanged. Back up wp-config.php before editing it.

This line does not create the helper, choose the cache folder, or set the Nginx cache zone. Those are separate setup steps.

The three names that must match

Item Example What it does
Nginx cache zone example_cache Internal name Nginx uses to identify the site's cache
Nginx cache folder /sites/example.com/cache Folder containing saved page files; the helper must clear this exact folder
Installed purge helper /usr/local/sbin/purge-example-cache Script called by WordPress; its exact path must match both wp-config.php and the sudoers rule

You never enter the Nginx zone name in WordPress.

Before you begin: example names

The instructions below use imaginary values. Replace them for your site:

Example in this guide Replace with
example.com Your website's domain
siteuser The Linux account running that site's PHP-FPM process
www-data The Nginx worker account, if different
example_cache A unique cache-zone name for this site
/sites/example.com/cache This site's actual cache directory
/usr/local/sbin/purge-example-cache The unique helper path you choose for this site

You need SSH administrator (sudo) access. If you have never edited Nginx or sudoers before, do this on a staging server first, or ask a server administrator to perform the Linux steps. A mistake in a cache directory path or sudoers file can cause problems.

Installation overview: six steps

  1. Set up Nginx caching: choose a cache folder and a unique cache-zone name for the website.
  2. Create the cache folder: make sure the Nginx worker can access it.
  3. Install the purge helper: copy purge-wordpress-fastcgi-cache to /usr/local/sbin/purge-example-cache, and edit its CACHE_DIR setting to match the Nginx cache folder.
  4. Grant limited permission: add a sudoers rule allowing only the site's PHP-FPM account to run that exact helper as the Nginx worker.
  5. Install and connect WordPress: copy fastcgi-cache-purge.php to wp-content/mu-plugins/ and add the one-line RH_FASTCGI_PURGE_HELPER setting to wp-config.php.
  6. Test: confirm cache MISS → HIT, logged-in bypass, automatic purge on published changes, and the manual toolbar button.

The detailed commands are in sections 1–5 below. Use the same example helper path throughout the guide; do not mix it with the repository file's original name.

Why this was created

Nginx FastCGI page caching reduces repeat PHP-FPM and MySQL work for anonymous WordPress visitors. The standard WordPress Nginx cache plugin could not reliably remove cache files: Nginx cache directories were owned by the Nginx worker and could be inaccessible to the PHP-FPM user. Giving PHP broad filesystem permissions would weaken isolation. A restricted helper, invoked through an exact sudoers allowlist, solves this while retaining the Nginx worker's ownership of cache files.

The setup also provides automatic invalidation after public content changes and an administrator-only Purge Nginx Cache toolbar action. This avoids relying on a separate caching plugin with incompatible filesystem assumptions.

Components

  • fastcgi-cache-purge.php: WordPress must-use plugin. Automatic hooks, toolbar link, nonce/capability checks, manual purge notices.
  • purge-wordpress-fastcgi-cache: executable shell helper. Removes regular files and empty subdirectories inside one fixed cache directory, without removing its root.
  • Nginx FastCGI cache zone and site-specific PHP location.
  • Per-site RH_FASTCGI_PURGE_HELPER setting in wp-config.php; no edits to the shared MU plugin are required.
  • Exact sudoers rule permitting the site's PHP-FPM account to execute only its purge helper as the Nginx worker account.

Request and purge flow

Anonymous GET → Nginx → cache HIT → cached response
                      ↳ cache MISS → PHP-FPM/WordPress → cache populated
Logged-in/admin/API/query request → bypass Nginx page cache → PHP-FPM
Published content update → WordPress hook → MU plugin → sudo allowlist
    → helper as www-data → delete cache entries → next request MISS
Administrator toolbar click → nonce + capability check → same helper
    → redirect to dashboard with success/error notice

Prerequisites

  • Ubuntu server with Nginx, PHP-FPM, WordPress, and a working HTTPS virtual host.
  • A dedicated PHP-FPM Unix account per site (examples below use siteuser).
  • Nginx worker account www-data (verify with ps and nginx -T; change if different).
  • sudo, find, wp-cli (recommended), and PHP exec() enabled in the actual PHP-FPM pool.
  • Root/sudo SSH access. Backup and test your existing Nginx site file before editing.
  • A dedicated cache directory and cache zone per website. Never point two sites at the same cache directory.

Commands below are examples; replace example.com, siteuser, PHP socket, cache zone, and paths consistently. Run on a staging server first.

How the site selects its cache

The WordPress plugin does not set or reference the Nginx cache zone name. Nginx associates the zone with a cache directory; the WordPress plugin calls a site-specific helper, and that helper clears only its configured directory.

Setting Site A example Site B example
Nginx keys_zone site_a_cache:20m site_b_cache:20m
Nginx fastcgi_cache site_a_cache site_b_cache
Cache directory /sites/site-a.example/cache /sites/site-b.example/cache
Root-owned purge helper /usr/local/sbin/purge-site-a-cache /usr/local/sbin/purge-site-b-cache
WordPress RH_FASTCGI_PURGE_HELPER /usr/local/sbin/purge-site-a-cache /usr/local/sbin/purge-site-b-cache

Keep one unchanged copy of fastcgi-cache-purge.php in each site's mu-plugins directory. Configure the helper path in each site's wp-config.php and make its sudoers allowlist match exactly. Do not expose a cache directory or helper path through a WordPress Admin option or request parameter.

1. Configure a site-specific FastCGI cache

The fastcgi_cache_path directive must be in Nginx's http context (typically a file included from within http {}), not inside server {}:

fastcgi_cache_path /sites/example.com/cache levels=1:2 keys_zone=example_cache:20m inactive=60m;

Create the cache root with suitable permissions for the Nginx worker, e.g.:

sudo install -d -o www-data -g www-data -m 0750 /sites/example.com/cache

Ensure www-data can traverse /sites and /sites/example.com without granting access to unrelated site data. Use narrowly scoped directory permissions or ACLs where required. The PHP-FPM user does not need write access to the Nginx cache directory.

In the HTTPS site's server {} block, configure the cache behavior. Integrate with existing PHP location and security includes; do not add a duplicate location ~ \.php$ block:

# Example inside server {}:
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($request_uri ~* "/wp-admin/|/wp-json/|/xmlrpc.php|/wp-.*\.php|/feed/|index.php|sitemap(_index)?\.xml|/cart/|/checkout/|/my-account/") { set $skip_cache 1; }
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in|edd_items_in_cart|woocommerce_items_in_cart") { set $skip_cache 1; }

add_header Fastcgi-Cache $upstream_cache_status always;

# Add these lines to the site's EXISTING PHP FastCGI location:
# fastcgi_cache_bypass $skip_cache;
# fastcgi_no_cache $skip_cache;
# fastcgi_cache example_cache;
# fastcgi_cache_valid 200 301 60m;
# fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
# fastcgi_cache_background_update on;

Critical: Do not set fastcgi_ignore_headers Cache-Control Expires Set-Cookie;. Respect upstream instructions to prevent caching and session cookies. The example bypass patterns are a baseline, not a guarantee for every WordPress plugin. Audit membership, forms, checkout, logged-in content, authentication, custom REST routes, and other dynamic endpoints before enabling a long TTL. fastcgi_cache_valid sets validity only when upstream headers do not override it; inactive is an eviction period, not a guaranteed TTL.

If your site uses a different PHP location structure (named locations, try_files, proxying, etc.), adapt the directives to the actual handler. Do not blindly replace your current vhost.

Validate and reload:

sudo nginx -t && sudo systemctl reload nginx

2. Install the restricted purge helper

  1. Download or clone this repository onto the server. From the directory containing purge-wordpress-fastcgi-cache, install a site-specific copy with the command below.
  2. Set the helper's CACHE_DIR to exactly the cache folder configured in Nginx. These commands use the example values from above:
sudo install -o root -g root -m 0755 purge-wordpress-fastcgi-cache /usr/local/sbin/purge-example-cache
sudo sed -i "s|^CACHE_DIR=.*|CACHE_DIR='/sites/example.com/cache'|" /usr/local/sbin/purge-example-cache
sudo grep '^CACHE_DIR=' /usr/local/sbin/purge-example-cache

Expected final line: CACHE_DIR='/sites/example.com/cache'.

Explanation: install copies the repository script to its permanent location and sets its owner and permissions. sed edits the installed copy, not the repository file. grep lets you check the result. Never use a cache folder that belongs to a different website.

The helper checks that the cache root is a directory and not a symlink, rejects symlinks found inside it, deletes regular files, then deletes empty subdirectories. It deliberately preserves the cache root and its permissions. This is a full-zone filesystem purge, not selective per-URL invalidation. Do not edit it to run rm -rf on a user-supplied path.

Security considerations

The helper is designed for an ordinary dedicated cache directory controlled by administrators and the Nginx worker. It is not hardened against a malicious concurrent filesystem writer swapping paths during a purge; for a high-threat multi-tenant environment, use a stronger privileged service design. Keep the cache directory and its parent path outside the WordPress-writable tree where practical. Never let the WordPress account modify the helper or sudoers file.

3. Permit only the required sudo action

Run sudo visudo -f /etc/sudoers.d/example-fastcgi-purge and add one line (replace siteuser with the actual PHP-FPM user):

siteuser ALL=(www-data) NOPASSWD: /usr/local/sbin/purge-example-cache

Then:

sudo chmod 0440 /etc/sudoers.d/example-fastcgi-purge
sudo visudo -cf /etc/sudoers.d/example-fastcgi-purge
sudo -u siteuser sudo -n -u www-data /usr/local/sbin/purge-example-cache

Expected: Cache purge completed.

For multiple sites, create one helper per site, with a unique root-owned executable path, cache directory, and sudoers rule. Avoid a generic script that accepts arbitrary paths from the PHP-FPM user.

4. Install the WordPress MU plugin

sudo install -d -o siteuser -g siteuser -m 0750 /sites/example.com/public/wp-content/mu-plugins
sudo install -o siteuser -g siteuser -m 0640 fastcgi-cache-purge.php /sites/example.com/public/wp-content/mu-plugins/fastcgi-cache-purge.php

Required for this multi-site example: define the site-specific helper path in wp-config.php, before the line that loads wp-settings.php:

define('RH_FASTCGI_PURGE_HELPER', '/usr/local/sbin/purge-example-cache');

The helper was installed in section 2, and the matching sudoers rule was created in section 3. The plugin has a built-in fallback helper path, but do not rely on it for multi-site setups: explicitly set RH_FASTCGI_PURGE_HELPER for every website. This constant does not configure the Nginx zone name; that remains in Nginx.

sudo php -l /sites/example.com/public/wp-content/mu-plugins/fastcgi-cache-purge.php
sudo -u siteuser wp plugin list --status=must-use --path=/sites/example.com/public

MU plugins load automatically. The code uses exec() to invoke sudo -n, so failures do not prompt for a password. Failures are written to the PHP error log. The WordPress Admin toolbar item is shown only to users with manage_options; the action also checks the same capability and a WordPress nonce. Manual purges redirect to the dashboard with a success/error notice.

Automatic hooks

  • save_post on a published post/page/custom post type, excluding revisions and autosaves.
  • transition_post_status when entering or leaving publish.
  • Post deletion/trashing, comment actions, navigation menu updates, and theme switching.

The plugin avoids multiple successful automatic purges in one PHP request. It does not automatically detect every sitewide option, theme customization, plugin setting, asset, or direct database change. Use the manual purge button after such changes. Some deletion/comment hooks are intentionally broad and may purge more often than necessary. Cache clearing is site-wide, not limited to affected URLs.

5. Test the actual web flow

Warm the cache and inspect status:

for i in 1 2; do curl -sS -o /dev/null -D - https://example.com/ | grep -iE '^(HTTP/|Fastcgi-Cache:)'; done

Look for MISS then HIT; STALE and UPDATING can appear when stale-while-revalidate is active. With an existing warm cache, HIT immediately is normal.

Test a simulated login cookie (does not authenticate):

curl -sS -o /dev/null -D - -H 'Cookie: wordpress_logged_in_test=1' https://example.com/ | grep -iE '^(HTTP/|Fastcgi-Cache:)'

Expected: BYPASS.

Test exclusions (/wp-admin/, /wp-json/, /feed/, /?s=test) and ensure none returns HIT. Then:

  1. Update a published page in WordPress Admin; immediately check for MISS then HIT.
  2. Save an unpublished draft; check that the already-warm public homepage remains HIT.
  3. Click Purge Nginx Cache in the Admin toolbar; confirm the green success notice and check MISS then HIT.
  4. Check PHP-FPM and Nginx error logs for failed helper calls.

If the first request after a purge is already HIT, a concurrent request may have rebuilt the cache. To conclusively test, coordinate with traffic or inspect cache files before and after purging.

6. Manual SSH purge and operations

sudo -u siteuser sudo -n -u www-data /usr/local/sbin/purge-example-cache

No Nginx reload is required for purging. Keep Nginx reloads for configuration changes. If using a CDN, note that purging origin FastCGI cache does not purge CDN edge cache; clear the CDN separately if needed.

Troubleshooting

Symptom What to check
BYPASS on public pages Cookies, query strings, path exclusions, upstream Cache-Control
No HIT Response Set-Cookie, upstream cache headers, missing cache directives, cache directory permissions
WordPress reports purge failure PHP-FPM exec(), sudoers exact path, helper permissions, PHP error log
sudo: a password is required NOPASSWD rule, actual PHP-FPM user, helper path
Permission denied Parent directory traversal and Nginx worker ownership
Cache contains symbolic links Investigate unexpected cache contents; do not weaken the guard blindly
Changes still look stale CDN cache, browser cache, WordPress object cache, missed purge hook
STALE or UPDATING Expected with background updates and stale-while-revalidate

7. Adding a second website

Repeat the installation separately for each site. For example, second.example could use:

Nginx zone:      second_cache
Nginx folder:    /sites/second.example/cache
Installed helper:/usr/local/sbin/purge-second-cache
WordPress line:  define('RH_FASTCGI_PURGE_HELPER', '/usr/local/sbin/purge-second-cache');

Copy the repository's same purge-wordpress-fastcgi-cache script again, this time to /usr/local/sbin/purge-second-cache; change its CACHE_DIR to /sites/second.example/cache; and authorize the second site's PHP-FPM user in its own sudoers rule. Install the same WordPress plugin on the second site. The sites must not share a cache folder or purge helper.

8. Migration and security checklist

  • [ ] Verify PHP-FPM pool user and Nginx worker user.
  • [ ] Configure separate Nginx cache zone and directory for each site.
  • [ ] Preserve existing TLS, redirects, HTTP/2 or HTTP/3, and security rules.
  • [ ] Respect Cache-Control, Expires, and Set-Cookie.
  • [ ] Audit custom dynamic routes and authentication/session cookies.
  • [ ] Install root-owned, site-specific helper and exact sudoers allowlist.
  • [ ] Install MU plugin and confirm php -l and WordPress registration.
  • [ ] Test caching, bypass, automatic purge, manual purge, and error logs.
  • [ ] Document any site-specific hooks required for plugin settings or custom content.
  • [ ] Keep a rollback copy of the prior Nginx config and purge setup.
  • [ ] Deploy on staging first; never assume that cache exclusions are universal.

Rollback

Remove/rename the MU plugin to disable its hooks and toolbar action. Restore the previous Nginx vhost (or remove the FastCGI cache directives), then run nginx -t and reload. Remove the site's sudoers allowlist and helper when no longer needed. Do not delete the cache root until Nginx no longer references it.

Attribution

Created by ChatGPT for Raw Hasan. The initial solution was developed and validated iteratively through WordPress Admin, WP-CLI, PHP-FPM, and Nginx tests. This repository version is generalized for reuse; review and adapt it before production deployment.