If you run WordPress behind Cloudflare, publishing a new post doesn’t always mean visitors see it right away — cached HTML, images, and feed data can keep serving the old version until the cache clears on its own. A custom save_post hook that calls Cloudflare’s purge API directly solves this by clearing only the URLs that actually changed, the moment you hit publish.
Why a Targeted Purge Beats a Full Cache Wipe
The instinct many site owners have is to just purge everything on every update, but that’s more disruptive than it needs to be. Every time you update or publish a post, you don’t want to purge your entire cache — that would be like repainting your whole house because one room got a new lamp. Purging only the specific URLs affected by a change — the post itself, its featured image, your feed — keeps the rest of your cached site untouched and fast for everyone else.
WordPress makes this possible through its built-in hook system. WordPress hooks, particularly the save_post action, are a powerful mechanism for triggering custom functions during key events like saving or updating a post, which is exactly the trigger point needed to fire off a Cloudflare purge request the instant a post goes live.
Getting Your Cloudflare Credentials Ready
Before writing any code, you need two pieces of information from your Cloudflare account: your Zone ID and an API Token scoped specifically for cache purging.
Using an API Token rather than a full Global API Key matters here. To communicate securely with Cloudflare’s API, you need a Bearer Token with appropriate permissions set, ensuring that only authorized applications can trigger cache purges on your behalf. It’s crucial to set the correct scopes, specifically granting permission to purge cache while restricting destructive or administrative actions — using scoped tokens instead of your full account key is the safer, more modern practice Cloudflare itself recommends.
Never hardcode these values directly into your theme files. Store your Zone ID and API Token in wp-config.php as defined constants, or better yet, as environment variables if your hosting setup supports them. A leaked API token embedded in a public repository, a shared snippet, or a theme export is a real security risk — anyone who gets hold of it can purge (or worse, depending on scope) your Cloudflare configuration.
The Hook Itself
Here’s a minified version of a working save_post hook that purges the current post’s URL, its associated image, and your feed on every publish — with your actual Zone ID and API Token replaced by placeholders:
function cloudflare_purge_post_url($post_ID,$post){if(!is_object($post))return;if(defined('DOING_AUTOSAVE')&&DOING_AUTOSAVE)return;if(wp_is_post_revision($post_ID))return;if(!in_array($post->post_type,array('post','page')))return;if($post->post_status!=='publish')return;$slug=get_post_field('post_name',$post_ID);$url="https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/purge_cache";$headers=array("Authorization: Bearer YOUR_CLOUDFLARE_API_TOKEN","Content-Type: application/json");$files=array(get_permalink($post_ID),home_url('/wp-content/uploads/'.$slug.'.jpg'),"https://sopriza.com/feed/atom/");$exclude=array("https://sopriza.com/favicon.webp","https://sopriza.com/wp-content/uploads/sopriza.jpg","https://sopriza.com/wp-content/themes/hybridmag/assets/css/font-figtree.css","https://sopriza.com/wp-content/uploads/today-football-predictions.jpg","https://sopriza.com/");$files=array_values(array_filter($files,function($u)use($exclude){foreach($exclude as $e){if($u===$e||strpos($u,'https://sopriza.com/author/')===0)return false;}return true;}));update_post_meta($post_ID,'_slug_purged',$slug);$data=json_encode(array("files"=>$files));$ch=curl_init($url);curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);curl_setopt($ch,CURLOPT_POST,true);curl_setopt($ch,CURLOPT_HTTPHEADER,$headers);curl_setopt($ch,CURLOPT_POSTFIELDS,$data);$response=curl_exec($ch);curl_close($ch);error_log('Cloudflare purge response: '.$response);}add_action('save_post','cloudflare_purge_post_url',10,2);
Replace YOUR_ZONE_ID and YOUR_CLOUDFLARE_API_TOKEN with references to your stored constants or environment variables (for example, getenv('CLOUDFLARE_ZONE_ID') and getenv('CLOUDFLARE_API_TOKEN')) rather than pasting real values directly into the string — that keeps your credentials out of your theme files and version control entirely.
Breaking Down What the Hook Actually Does
- Guard clauses first. The function immediately bails out on autosaves, post revisions, non-post/page content types, and anything not in a
publish status — this prevents unnecessary API calls firing on every draft save or minor revision.
- Builds a targeted file list. Rather than purging the entire zone, it only sends the specific post’s permalink, a guessed image URL based on the post slug, and the site’s Atom feed — keeping the purge scoped and fast.
- Excludes specific URLs. A hardcoded exclude list filters out static assets — like a favicon or theme CSS — that rarely change and don’t need repeated purging, along with author archive pages matched by URL pattern.
- Tracks purge state. It saves a
_slug_purged post meta value, giving you a way to check later whether a given post has already triggered a purge.
- Sends the request via cURL. The actual API call posts a JSON payload of files to Cloudflare’s purge endpoint, then logs the raw response to your PHP error log for debugging.
Where This Approach Falls Short of a Full Plugin
For simple use cases, a hand-rolled hook like this works well, but it’s worth knowing its limits compared to established solutions. The official Cloudflare WordPress plugin’s Automatic Cache Management feature purges associated URLs automatically whenever a post, page, attachment, or comment is added, edited, or deleted, and also clears cache automatically when you switch or customize a theme — coverage a single custom function won’t replicate without significant extra code.
There’s also a known gap with scheduled posts. Automatic purge hooks built around simpler WordPress actions don’t always fire correctly when a post is scheduled to publish later via WP-Cron, since the “future publish” transition uses a different hook than a manual publish action — worth testing specifically if you rely on scheduled publishing.
Common Mistakes to Avoid
- Hardcoding your API token or Zone ID directly in the function. Always reference environment variables or
wp-config.php constants instead.
- Forgetting to test scheduled posts separately. A hook that works for manual publishing may silently fail to fire on WP-Cron-triggered scheduled posts.
- Purging too broadly. Wiping your entire cache on every save adds unnecessary load and slows down the next visitor’s page load while cache rebuilds — targeted URL purging avoids this.
- Ignoring the error log. The
error_log call in this function is there for a reason — check it after publishing to confirm the purge request actually succeeded rather than assuming it worked.
Join The Discussion
Have you set up a custom Cloudflare purge hook on your own WordPress site, or do you rely on the official plugin instead? Share what your setup looks like, especially if you’ve had to work around the scheduled-post purge gap, and if you’ve found a cleaner way to store and reference your API credentials, it’d be great to hear how you’ve structured that for security.