Using PHP with WordPress to Purge a Single File From CloudFlare

I recently upgraded my hosting plan to Bluehost Pro because I wanted faster server response times. For the most part, it’s good. But I also use CloudFlare to serve static files like css and javascript along with the W3 Total Cache plugin because they have servers all over the world that can deliver content to users faster than mine ever could.

CloudFlare allows you to choose the level of caching and for my WordPress site, I set it to “Aggressive” so even css files referenced by query strings like those used in the Atahualpa theme are delivered using CloudFlare. The results are impressive and a lot of testing shows that my site speed is greatly improved.

But the weak link in the chain is the HTML itself. I would love CloudFlare to store my entire site on its servers – not just the static content like images, css and js. Lately I found out that this option is available using something known as “Page Rules”. With my free plan I can use three rules and configure them so that it caches everything on my blog – including HTML. Here’s what it looks like.

Cache Everything with CloudFlare - Including HTML
Cache Everything with CloudFlare – Including HTML

This pattern uses the wildcard “*” to tell CloudFlare that I want this rule to apply to all the pages on my blog. For these pages, I select the “Cache Everything” option, tell the Edge servers to refresh the content every 2 hrs and set the Browser cache expiry to 1 year. In practice, I’ll also exclude all my admin pages by creating another rule in front of this one with the pattern *bhagwad.com/blog/wp* and tell CloudFlare to bypass the cache for them.

Anyway, this works great. You’ll see your site speed zoom so fast you won’t be sure whether it even reloaded. It’s that good. Problem solved? Not really.

While most of the content on my blog is static, I get quite a few comments and these present a problem. When a visitor leaves a comment, they expect it to show up immediately. But CloudFlare’s servers continue to serve up the old page and so it looks as if their comment was eaten without a trace. Sure, it’ll eventually show up but it’s a very poor experience for those visitors who truly add value to your site by leaving a comment in the first place.

Ideally you’d like CloudFlare to instantly refresh its page cache for that one single page after the user posts a comment. You can manually purge pages under “CloudFlare Settings” for your site, but this obviously won’t work. Fortunately CloudFlare provides an API to do just this. You just need to get your hands dirty with a bit of PHP code.

But first, a warning. I’m not a pro PHP programmer. I can code in several languages, so it’s easy for me to pick up what’s happening and write some code of my own. But I’ve never actually written a program in PHP, so be careful! Go through this simple code carefully yourself to see if anything will go wrong. All I can say is that it seems to work fine for me. So let’s dive in!

The CloudFlare APIs give you a broad idea of how to purge a single file of your site from the CloudFlare cache. But the documentation is less than helpful in that it doesn’t give you the actual PHP syntax. Look at section 4.5 – “zone_file_purge” — Purge a single file in CloudFlare’s cache. That code there doesn’t work if you just paste it into PHP. I tried, got a big fat error, and had to retreat with my tail between my legs.

Apparently you need to use something called “curl” (cURL) to make this request and I had to split my head for hours figuring out what it is and how to write the damn code. Support for curl was enabled on my Bluehost shared hosting plan – just check your PHP configuration and do a Ctrl+F for “curl”.

I thought the best place to put this PHP code was at the end of my theme’s “functions.php” file. This way when I upgrade WordPress, my changes won’t be wiped out and I never plan on upgrading my theme. So we need to add a function that will run whenever someone posts a valid comment and tell CloudFlare to purge that particular file from it’s cache. Here it is:

function cloudflare_purge_file( $post_ID )
{
$fields = array(
'a' => 'zone_file_purge',
'tkn' => 'your token key',
'email' => 'your email',
'z' => 'your domain',
'url' => get_permalink()
);

//url-ify the data for the POST
foreach($fields as $key=>$value) { $fields_string .= $key.'='.$value.'&'; }
rtrim($fields_string, '&');

//open connection
$ch = curl_init();

//set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, 'https://www.cloudflare.com/api_json.html');
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);

//execute post
$result = curl_exec($ch);

//close connection
curl_close($ch);
$friends = 'your email';
wp_mail( $friends, "File purged", $result );

return $post_ID;
}

Like any good programmer, much of this code is just heavily modified from elsewhere and repurposed to this task. I try and avoid typing in actual code myself as much as possible :). The comments are not mine either. But what’s happening is simple. We first create the data structure holding the necessary information required by the CloudFlare API. The keys ‘a’, ‘tkn’, ’email’, ‘z’ and ‘url’ hold “zone_file_purge”, the token key, the email, the domain, and the url we want to purge. For this last value, I simply use the get_permalink() function defined in the file. The other values are those in your CloudFlare settings. You have to use the same email registered with your site CloudFlare. You can find your token API key under “Account” on the CloudFlare website.

The next part of the code formats the data field, initiates the curl connection to “https://www.cloudflare.com/api_json.html”, and passes in the paramters ending with “curl_exec()”. We store the result in $result.

The last part emails you with the output. It helps you see whether the request succeeded or failed. The response format is as specified in the CloudFlare API page and looks something like this if it succeeded:

{“request”:{“act”:”zone_file_purge”},”response”:{“vtxt_match”:null,”url”:”http:\/\/www.bhagwad.com\/blog\/2009\/haikus\/haikus-morning-serial.html\/”},”result”:”success”,”msg”:null}

If it didn’t succeed, the error message will tell you what went wrong and help you diagnose the problem.

So paste your code at the end of “functions.php” in your theme (before the closing “?>” php tag naturally). Now we just need to tell WordPress to run this whenever a comment is posted. So under that function, add:

add_action( 'comment_post', 'cloudflare_purge_file' );

This “hooks” into the comment post procedure and runs it each time a user submits a valid comment. You’re done and you should get an email each time a comment is posted letting you know a file was purged. After you’re sure it’s working, delete the two lines that send the email to stop getting notified and you should be set!

Ideally someone should just turn this into a WordPress plugin, but I don’t know enough about WordPress internals or PHP to do this and I’m not in the mood to learn either. This post itself is a useful reference for myself later on if I forget how to do it or need to revert the changes.

I’m still not able to get the page to return the most recent value immediately after a person submits a comment. But it does get the job done. Let me know whether or not this works for you.

What do you think of this post?
  • Agree (3)
  • Don't Agree but Interesting (0)
  • You're an asshole (0)

12 thoughts on “Using PHP with WordPress to Purge a Single File From CloudFlare”

  1. Thanks Bhagwad. I’ve just started to explore CloudFlare in more detail for my WordPress blogs, and having read your post, I can see you’re a way ahead of me!

    I have two questions.

    First, do I interpret your post correctly in concluding that setting a Page Rule as “aggressive” caches everything BUT the HTML (the content and comments)? ie, CSS, javascript, images.

    Second, I’m not as savvy on PHP as you. So do you know if anyone has taken your idea and made a WordPress plugin of it?

    Best regards, Philip.

    Reply

Leave a Comment