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.
- 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
When content changes, immediately purges post permalink, homepage, post type archive, and related API endpoints. This happens when posts are published, edited, or deleted.
Collects and batch-processes site feed, taxonomy archives, author archives, API endpoints, XML sitemaps, and URL prefixes.
When taxonomy terms (categories, tags, custom taxonomies) are created, updated, or deleted, automatically purges their archive pages, parent terms, and related endpoints.
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"
When you hit "Publish", the plugin immediately purges these URLs:
-
The article permalink:
https://sportsgazette.com/local-sports-team-wins-championship/
-
Homepage:
https://sportsgazette.com/
-
Post type archive:
https://sportsgazette.com/blog/orhttps://sportsgazette.com/news/(depends on your setup)
-
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.
Simultaneously, the plugin queues these related URLs for batch processing:
-
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)
-
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)
-
Custom taxonomy archives:
https://sportsgazette.com/region/downtown/(as URL prefix)https://sportsgazette.com/region/downtown/feed/(as exact URL)
-
Author archive:
https://sportsgazette.com/author/writer-name/(as URL prefix)https://sportsgazette.com/author/writer-name/feed/(as exact URL)
-
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
-
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 likepost-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
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.
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":
-
The term archive page:
https://sportsgazette.com/category/sports/(as exact URL)
-
Homepage:
https://sportsgazette.com/
-
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)
-
Term archive page:
https://sportsgazette.com/category/sports/(as URL prefix, to catch pagination)
-
Term feeds:
https://sportsgazette.com/category/sports/feed/(as exact URL)
-
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)
-
Site-wide feeds:
https://sportsgazette.com/feed/(as exact URL)
-
XML sitemaps (when Yoast SEO is active):
https://sportsgazette.com/sitemap_index.xml(as exact URL)sportsgazette.com/category-sitemap(as URL prefix)
- The plugin adds all URLs to a queue, removing any duplicates
- Every minute, WP-Cron triggers the queue processing
- The plugin takes a batch (default 30 URLs) from the front of the queue
- It sends these URLs to Cloudflare's API for purging
- If successful, these URLs are removed from the queue
- The process continues until the queue is empty
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.
- Download the latest release ZIP file from the Releases page
- In your WordPress admin, go to Plugins → Add New → Upload Plugin
- Choose the downloaded ZIP file and click "Install Now"
- Activate the plugin after installation completes
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 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
Filepurge - Site, post-comment, term, and author feeds
- Exact sitemap URLs such as
sitemap_index.xmland 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:
Prefixpurge 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 separateFileitem; 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.
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 32Provide 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.
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 );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 );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.
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.
- 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
- 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
For support, open an issue in the plugin repository. Released under GPLv3 license.