When you have a WordPress site on Cloudflare with a Cache Everything page rule, you probably know this: everything includes preview pages!

This post suggests a way to make sure Cloudflare does not cache the preview page even when you set your Cloudflare cache level to “cache everything” with a page rule.

Cache Everything

So what is “cache everything”? By default, Cloudflare caches static files, such as images, CSS, and JavaScript files, but does not cache HTML.

Since the HTML is generated dynamically by WordPress, every visit runs the whole WordPress machine to generate the HTML for a page, even the if the content of the page is static. That causes most WordPress sites to become slow, especially when it comes to “time to first byte”, or TTFB, And TTFB is important because it impacts the final page speed.

To speed things up, you can request that Clouflare cache the HTML, so when visitors come to a page that has been recently requested, Clouflare returns the HTML it has in cache, instead of requesting your server to generate it again. And that can have a dramatic effect on your site’s speed.

But everything means everything, and that includes the pages generated by WordPress when you are editing a post or page and request a “preview” of what you are doing. The first time you see the preview, it shows what you are working on. The second time, it will show the page as cached by Cloudflare, without the most recent modifications you may have done to your page.

Busting the Cache

You can change this behavior by adding a small inline JavaScript that will be cached with the HTML. The JavaScript then will generate a random code and a button that, when clicked, will reload the page with the random code as a URL query string parameter. This way you can continue previewing your post without having to stop to do a manual purge of the page you are working on, or put Cloudflare on development mode (which can slow down the whole site for all its visitors while it’s on.)

Normally, a preview page will have an URL like this:

example.com/path-to-page/?preview=true

One way to avoid it being cached by Cloudflare under a cache-everything page rule is to add another page rule:

example.com*?preview=true*
Settings:
Cache Level: Bypass

But that means you need to spend one of your plan’s allocated page rules with something that will be of occasional use. Also, it only benefits the person who’s editing the page or post, its author. Instead, if you avoid this page rule, you use the entitlement instead for something that impacts the whole site, such as a canonical redirect from naked domain to www, or vice-versa.

This is especially important if you have a small WordPress website running on a Cloudflare Free Plan, which only allows for 3 page rules (BTW, check our recommended solution to implement cache everything on a WordPress site with static content using only 2 page rules.)

A Cache Buster Just for Preview

You can insert a small HTML containing an inline JavaScript code into your WordPress pages to solve this issue. The HTML/JavaScript will be cached by Cloudflare, and the code to bust the cache will work when cache everything is implemented.

<!-- Random string generator adapted from: https://www.geeksforgeeks.org/generate-random-alpha-numeric-string-in-javascript/ -->
<div style="text-align:center;">
	<!-- "display: none;" will be unset below if page has preview query string -->
	<button id="preview-only" style="display: none;" onclick="add_cacheBusterKey()"> 
		bust preview cache
	</button>
	<script>
	var kv = 'preview=true';
	var url = window.location.href;
	// tests whether 'preview=true' is found in the URL
	if(url.indexOf( kv ) != -1) {
		// then shows cache buster button
		document.getElementById("preview-only").style.display = "unset";

		// create random string
		function randomStr(len, arr) { 
			var ans = ''; 
			for (var i = len; i > 0; i--) { 
				ans += 
				arr[Math.floor(Math.random() * arr.length)]; 
			} 
			return ans; 
		} 
		// appends random &random-string to URL
		function add_cacheBusterKey() { 
			cacheBusterKey = randomStr(5, '12345abcde'); 
			window.location.href=window.location.href + '&' + cacheBusterKey;
		} 
	};
	</script>
</div>

The HTML contains a hidden button and the JS code will check whether the preview= query string is present. If it is, it will display the button, with an onclick() element that will reload the page and add a random string as a second query string value.

So your preview page that initially had an URL like:

https://example.com/some-page-or-post/?preview=true

Will be reloaded as:

https://example.com/some-page-or-post/?preview=true&a1b2c3

where a1b2c3 is the random alphanumeric string added to it.

Since Cloudflare treats each page with a distinct query string as a separate page for caching purposes when you set the cache level to “cache everything”, the new URL will be fetched straight from your origin, showing the preview of the page or post you are working on.

Adding the HTML to WordPress

There are a few ways you can add the HTML above to your WordPress site.

Plugins

The easiest way is to make use of one of the several plugins in the wordpress.org plugin repository that let you add code to your pages and posts.

Ideally, the plugin should already come with the option to only show in the preview pages (in this case you should use the HTML below).

Custom HTML Widget

You can also add the HTML to a WordPress widget of the type Custom HTML, though this solution has its limitations. It’s important not to set a title, otherwise the title would show to all pages, preview or not. Also, depending on the theme your site is using, the HTML should be present in every page, but its content (the “preview cache bust” button) will only display for pages with the preview=true in the URL as part of the query string.

When testing this solution in a standard WordPress installation with the Twenty Twenty default theme, it worked fine. But depending on what theme you use, you may need to make sure the appearance of non-preview pages is not altered by the conditional widget we just added. If some element appears where they shouldn’t, like white backgrounds where the widget should display, for instance, you may want to read on and see if the solution involving adding a couple of PHP lines to one or more of your child theme files may work for you.

A Better Way

If your site has (as it should) a child theme, you may want to benefit from WordPress’ built-in function is_preview() to both (1) add the cache-busting button only to preview pages and posts, and (2) streamline the HTML/JavaScript to be added.

In a default WordPress installation with the Twenty Twenty theme, you could for instance edit the header.php file of your child theme and add the following PHP code right below the HTML <body> tag.

<?php if ( is_search() ) : ?>
The streamlined HTML goes here
<?php endif ; ?>

This if condition will evaluate if the page being generated is a preview page, and only run the code between the if/endif directives if it is. In our case, the code is the HTML with the inline JavaScript. With the if condition, no hidden buttons will need to appear in the HTML of other pages, avoiding any possible CSS issues for those other pages.

Placing it right below the <body> tag will assure that the button will show at the very top of the page, and not interfering otherwise with the page layout.

For sites with other themes, you should place the PHP lines immediately after the HTML <body> tag, wherever the theme template files would insert it. (If you don’t know where that is, you can use the excellent String Locator plugin to quickly find which of your child theme’s files adds the <body> tag. Ignore results for CSS files, and you should find it right away.)

The streamlined HTML with JavaScript

<!-- Random string generator adapted from: https://www.geeksforgeeks.org/generate-random-alpha-numeric-string-in-javascript/ -->
<!-- This version inserted by PHP that tests if is_preview() to only insert the below HTML in preview pages -->
<div style="text-align:center;">
	<button id="preview-only" onclick="add_cacheBusterKey()">
		bust preview cache
	</button>
	<script>
	// create random string
		function randomStr(len, arr) { 
			var ans = ''; 
			for (var i = len; i > 0; i--) { 
				ans += 
				arr[Math.floor(Math.random() * arr.length)]; 
			} 
			return ans; 
		} 
	// appends random &random-string to URL
		function add_cacheBusterKey() { 
			cacheBusterKey = randomStr(5, '12345abcde'); 
			window.location.href=window.location.href + '&' + cacheBusterKey;
		} 
	</script>
</div>

Comments

Please send any comments as replies to the tweet below. Click on the date of the tweet to open it on Twitter.

By cbrandt