Skip to content

Repository files navigation

ZuidWest Cache Manager

A WordPress plugin for efficient Cloudflare cache management. Immediately purges high-priority URLs and queues others for batch processing via WP-Cron when content changes.

Features

  • Smart URL Detection: Automatically purges web URLs and REST API endpoints
  • Priority-Based Processing: Immediate critical purges, queued less-critical URLs
  • Batch Processing: Configurable batches to avoid API rate limits
  • Admin Interface: Connection testing, queue management, debugging
  • WP-Cron Integration: Reliable scheduled processing
  • URL Prefix Purging: Uses Cloudflare's prefix purging for archives, automatically clearing paginated pages (v1.1+)
  • Taxonomy Term Handling: Purges cache when taxonomy terms are created, edited, or deleted (v1.3+)
  • SEO Sitemap Purging: Purges Yoast SEO sitemaps — including the News and Video add-ons — when content changes, with filters to plug in other SEO plugins (v1.8+)
  • Cache Warming (opt-in): After a successful purge, queues the affected page URLs for a background WP-Cron refetch so the cache is repopulated without a visitor paying the render

How It Works

Immediate Purging (High Priority)

When content changes, immediately purges post permalink, homepage, post type archive, and related API endpoints. This happens when posts are published, edited, or deleted.

Queued Purging (Low Priority)

Collects and batch-processes site feed, taxonomy archives, author archives, API endpoints, XML sitemaps, and URL prefixes.

Taxonomy Term Handling

When taxonomy terms (categories, tags, custom taxonomies) are created, updated, or deleted, automatically purges their archive pages, parent terms, and related endpoints.

Example: How It Handles Content Changes

Post Updates

When a WordPress post with multiple taxonomies is published or updated, the plugin intelligently manages Cloudflare cache purging in a multi-tiered approach. Here's a specific example scenario:

Let's say you have a sports news website (sportsgazette.com) and you publish a new article titled "Local Sports Team Wins Championship" with:

  • Post type: post
  • Categories: "Sports", "Local News"
  • Tags: "Championship", "Basketball", "City Events"
  • Custom taxonomy: "Regions" with term "Downtown"

Immediate High-Priority Purging

When you hit "Publish", the plugin immediately purges these URLs:

  1. The article permalink:

    • https://sportsgazette.com/local-sports-team-wins-championship/
  2. Homepage:

    • https://sportsgazette.com/
  3. Post type archive:

    • https://sportsgazette.com/blog/ or https://sportsgazette.com/news/ (depends on your setup)
  4. REST API endpoints (direct access):

    • https://sportsgazette.com/wp-json/wp/v2/posts/1234/ (article API endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/posts/ (posts collection)
    • https://sportsgazette.com/wp-json/ (API root)

These high-priority purges happen immediately to ensure the most critical URLs are fresh.

Queued Low-Priority Purging

Simultaneously, the plugin queues these related URLs for batch processing:

  1. Category archives:

    • https://sportsgazette.com/category/sports/ (as URL prefix)
    • https://sportsgazette.com/category/local-news/ (as URL prefix)
    • https://sportsgazette.com/category/sports/feed/ (as exact URL)
    • https://sportsgazette.com/category/local-news/feed/ (as exact URL)
  2. Tag archives:

    • https://sportsgazette.com/tag/championship/ (as URL prefix)
    • https://sportsgazette.com/tag/basketball/ (as URL prefix)
    • https://sportsgazette.com/tag/city-events/ (as URL prefix)
    • Related tag feeds (as exact URLs)
  3. Custom taxonomy archives:

    • https://sportsgazette.com/region/downtown/ (as URL prefix)
    • https://sportsgazette.com/region/downtown/feed/ (as exact URL)
  4. Author archive:

    • https://sportsgazette.com/author/writer-name/ (as URL prefix)
    • https://sportsgazette.com/author/writer-name/feed/ (as exact URL)
  5. REST API related endpoints:

    • https://sportsgazette.com/wp-json/wp/v2/categories/5/ (Sports category endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/categories/7/ (Local News category endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/tags/12/ (Championship tag endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/tags/15/ (Basketball tag endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/tags/22/ (City Events tag endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/regions/3/ (Downtown region endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/users/42/ (Author endpoint)
    • All taxonomy collection endpoints
  6. XML sitemaps (when Yoast SEO is active):

    • https://sportsgazette.com/sitemap_index.xml (as exact URL)
    • https://sportsgazette.com/post-sitemap.xml (as exact URL)
    • sportsgazette.com/post-sitemap (as URL prefix, to catch paginated variants like post-sitemap2.xml)
    • sportsgazette.com/author-sitemap (as prefix, for post types with author support)
    • News and Video sitemaps when the Yoast News/Video add-ons are active

Post Deletion Handling

When a post is deleted or moved to trash, the plugin handles cache purging intelligently:

  • Identifies if a post has a trashed status
  • Attempts to reconstruct the original permalink by removing "__trashed" suffix
  • Purges the post's permalink from cache
  • Purges related feeds and archives
  • Cleans up any API endpoints related to the deleted post

This ensures that when content is removed (whether temporarily or permanently), the cache is properly updated to reflect these changes.

Taxonomy Term Updates

When a taxonomy term is created, edited, or deleted, the plugin performs similar intelligent cache purging. For example, if you update a category called "Sports":

Immediate High-Priority Purging

  1. The term archive page:

    • https://sportsgazette.com/category/sports/ (as exact URL)
  2. Homepage:

    • https://sportsgazette.com/
  3. REST API endpoints:

    • https://sportsgazette.com/wp-json/wp/v2/categories/5/ (Sports category endpoint)
    • https://sportsgazette.com/wp-json/wp/v2/categories/ (Categories collection)
    • https://sportsgazette.com/wp-json/wp/v2/taxonomies/ (Taxonomies endpoint)

Queued Low-Priority Purging

  1. Term archive page:

    • https://sportsgazette.com/category/sports/ (as URL prefix, to catch pagination)
  2. Term feeds:

    • https://sportsgazette.com/category/sports/feed/ (as exact URL)
  3. Parent term archives (if applicable):

    • https://sportsgazette.com/category/parent-category/ (as both file and prefix)
    • https://sportsgazette.com/category/parent-category/feed/ (as exact URL)
  4. Site-wide feeds:

    • https://sportsgazette.com/feed/ (as exact URL)
  5. XML sitemaps (when Yoast SEO is active):

    • https://sportsgazette.com/sitemap_index.xml (as exact URL)
    • sportsgazette.com/category-sitemap (as URL prefix)

How the Queue Processing Works

  1. The plugin adds all URLs to a queue, removing any duplicates
  2. Every minute, WP-Cron triggers the queue processing
  3. The plugin takes a batch (default 30 URLs) from the front of the queue
  4. It sends these URLs to Cloudflare's API for purging
  5. If successful, these URLs are removed from the queue
  6. The process continues until the queue is empty

The Benefit of URL Prefix Purging

For archives, the plugin uses Cloudflare's URL prefix purging. This is especially valuable for handling paginated pages. For example, when purging sportsgazette.com/category/sports/, it also automatically clears:

  • sportsgazette.com/category/sports/page/2/
  • sportsgazette.com/category/sports/page/3/
  • sportsgazette.com/category/sports/page/4/
  • And all other pagination pages

Without URL prefix purging, you would need to individually purge each pagination URL, which is inefficient and might miss some pages. The prefix approach ensures that all paginated archive pages are properly refreshed when content changes, improving both cache efficiency and ensuring visitors always see up-to-date content.

Installation & Configuration

Download the latest release

  1. Download the latest release ZIP file from the Releases page
  2. In your WordPress admin, go to Plugins → Add New → Upload Plugin
  3. Choose the downloaded ZIP file and click "Install Now"
  4. Activate the plugin after installation completes

Configuration

Configure the plugin under Settings → ZuidWest Cache:

  • Zone ID: Cloudflare Zone ID
  • API Key: Cloudflare API key with cache purging permissions
  • Batch Size: URLs per batch (default: 30)
  • Extra Domains: Comma-separated list of additional domains to purge (e.g., app.example.com,www.example.com). URLs will be duplicated for these domains.
  • Warm Cache After Purge: When enabled, queues eligible exact-URL purges for a background refetch, so the CDN/origin cache is repopulated by the server instead of by a visitor. See Cache warming selection for the exact rules. Off by default.
  • Debug Mode: Enable logging

Cache warming selection

Cache warming is event-driven and does not crawl the whole site. After a successful Cloudflare purge, the warmer queues every purge item of type File except URLs under the WordPress REST API path. This includes exact URLs such as:

  • Post and page permalinks, the home page, and archive landing pages when they are also emitted as a File purge
  • Site, post-comment, term, and author feeds
  • Exact sitemap URLs such as sitemap_index.xml and the first page of a post-type, taxonomy, author, news, or video sitemap
  • Eligible copies of these URLs for any configured extra domains

The warmer does not queue:

  • Prefix purge items. A prefix purge may invalidate an entire archive and its pagination, but the warmer only fetches an archive landing page when that URL is also present as a separate File item; it does not fetch /page/2/ and later pages.
  • WordPress REST API URLs

Pending URLs are deduplicated in a queue capped at 500 entries. The every-minute WP-Cron job fetches at most five queued URLs per run, after purge processing. Each request is an unauthenticated public GET unless a valid WAF warm token is configured. Transport failures move to the queue tail for a later retry; any HTTP response, including 4xx or 5xx, is considered terminal and dequeues the URL. Warming is best-effort: queue updates are not atomic, so under concurrent purges or overlapping cron runs a URL can occasionally be warmed more than once or a pending URL can be lost — a visitor then warms that page instead. Actual timing depends on WP-Cron traffic.

Authenticated Cloudflare WAF exception

Cache warming only populates the Cloudflare edge when the server reaches the site's public URL through Cloudflare. If a WAF rule or Super Bot Fight Mode blocks these requests, configure a private per-installation token before creating an exception. Never create a skip rule based only on the ZWCacheMan-Warmer user agent — it is trivially spoofable.

Generate a 32-byte token:

openssl rand -hex 32

Provide it through the server environment and expose it to WordPress in wp-config.php:

define(
    'ZW_CACHEMAN_WARM_TOKEN',
    getenv( 'ZW_CACHEMAN_WARM_TOKEN' ) ?: ''
);

The token must contain exactly 64 hexadecimal characters. It is never stored in the WordPress database or displayed by the plugin; the settings page only reports whether a valid token is configured.

After sending one warming request, identify the actual server egress IP and blocking rule in Cloudflare Security Events. Create the narrowest possible exception, for example:

(
  http.request.method eq "GET"
  and ip.src in {203.0.113.10}
  and any(
    http.request.headers["x-zw-cache-warm-token"][*]
      eq "<private-64-character-token>"
  )
)

Replace the example IP and token with the server's dedicated static egress IP and configured token. Keep match logging enabled and skip only the specific managed rule that caused the false positive, or only Super Bot Fight Mode when applicable. Do not skip all custom rules, all managed rules, or rate limiting. Do not use an IP-based exception with a shared or dynamic egress IP. Cloudflare Bot Fight Mode cannot be skipped with a custom WAF rule.

Developer Filters

zw_cacheman_sitemap_provider

Replace or disable the sitemap provider. The plugin auto-detects Yoast SEO; return your own CachemanSitemapProvider implementation to support another SEO plugin, or null to disable sitemap purging entirely.

add_filter( 'zw_cacheman_sitemap_provider', function ( $provider, $logger ) {
    return new My_RankMath_Sitemap_Provider( $logger ); // or null to disable
}, 10, 2 );

zw_cacheman_sitemap_items

Add, remove, or modify the computed sitemap purge items before they are queued. Receives the items plus a context array (event and post_type or taxonomy). Each item is an array with a url (absolute) and a type (ZW_CACHEMAN_Core\PurgeType case, or the string 'file'/'prefix'). Invalid items are logged and dropped.

add_filter( 'zw_cacheman_sitemap_items', function ( array $items, array $context ) {
    if ( 'post_change' === $context['event'] && 'event' === $context['post_type'] ) {
        $items[] = [
            'url'  => home_url( '/events-sitemap.xml' ),
            'type' => ZW_CACHEMAN_Core\PurgeType::File,
        ];
    }
    return $items;
}, 10, 2 );

Known Limitations

REST API URLs with Query Strings

The plugin only purges base REST API URLs without query parameters. URLs like /wp-json/wp/v2/posts?per_page=15&_fields=title,content won't be automatically purged.

Solution: Configure Cloudflare Cache Rules to ignore query strings for REST API endpoints.

Combined Taxonomy Feeds

The plugin purges individual taxonomy feeds (e.g., /regio/roosendaal/feed/) but not combined feeds with comma-separated terms (e.g., /regio/roosendaal,bergen-op-zoom/feed/).

Solution: Configure Cloudflare Cache Rules to handle these URL patterns appropriately.

Troubleshooting

  • Connection Issues: Test connection on settings page, verify API credentials
  • Queue Problems: Check queue processing, ensure WP-Cron is functional
  • Debug Logs: Enable Debug Mode, check PHP error log for "[ZW Cacheman]" entries
  • Common Issues: Firewall blocking API, WP-Cron configuration, additional caching layers

Requirements

  • WordPress 7.0+
  • PHP 8.3+
  • Active Cloudflare account with API access
  • A Cloudflare plan that supports URL prefix purging (verified on Business; file purging works on all plans)
  • Properly configured WP-Cron

Support & License

For support, open an issue in the plugin repository. Released under GPLv3 license.

About

A WordPress plugin for efficient Cloudflare cache management

Resources

Stars

0 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages