Websites often have content that rarely changes. Cloudflare offers a free service that hosts your content on 200+ servers around the world. As content changes, the cached copies are invalidated and fetched from the origin server. In this way, a website can handle millions of requests at high speed with a single core origin server.

There are a few Cloudflare page rules that enable caching of an entire website. The first rule is an exception that allows the admin area to be accessed properly. The second rule tells Cloudflare to cache everything on the edge servers for 30 days.

Let's say content changes on the website, for example a new blog post is published. There's an easy way to let Cloudflare know so all the edge servers get a new copy of the changed pages. Using the Purge Files by URL API method it's possible to tell Cloudflare to expire the cache for a single URL or multiple URLs. Each page on the website is slightly slower on the first page load but the page will be cached from then on. In the case of a blog post, the homepage URL, author URL and tag URLs would need to be purged. Depending on your web stack it might be straightforward to implement. On Ghost blogging software it can be done using multiple webhooks such as post.added, Ghost will send all the URLs you need in the request. If you want to easily inspect what is sent to the webhook endpoint I recommend https://webhook.site/.

For the sake of simplicity I used the Purge All Files method. My website is small enough and the server can handle the load, so it doesn't really matter if the entire cache is purged. I created a webhook in Ghost that fires when anything on the site changes, this particular webhook does not send any data about what changed. For the webhook endpoint I set up a simple Cloudflare worker which is included in the free plan. When the webhook fires, the worker gets a request and then sends a request to the Cloudflare API. Here's the Cloudflare worker code I used based off of the code I found here:

addEventListener('fetch', event => {

    event.respondWith(purgeCache(event.request))

})

async function purgeCache(request) {

    const url = new URL(request.url)
    const zone = url.searchParams.get('zone')
    
    let zoneIdValidated = (new RegExp("^([a-z0-9]{32})$")).test(zone)
    let keyValidated = (new RegExp("^SECRET_KEY$")).test(url.searchParams.get('key'))

    if (!keyValidated) {
        return new Response('Invalid auth', {
          status: 500
        })
    }

    if (!zoneIdValidated) {
        return new Response('Invalid Zone ID', {
          status: 500
        })
    }

    const data = {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer CLOUDFLARE_API_TOKEN_WITH_PURGE_PERMISSION'
        },
        body: '{"purge_everything":true}'
    }
    
    const response = await fetch(
        'https://api.cloudflare.com/client/v4/zones/' + zone + '/purge_cache',
        data
    )

    return response
}

The webhook endpoint will look like this: https://workername.yoursubdomain.workers.dev/?key=SECRET_KEY&zone=CLOUDFLARE_ZONE

You'll have to create a new integration in the Ghost admin area:

Propagation should take around 10 seconds after site content changes.

Final Thoughts

  • It would be interesting to implement a Ghost plugin that communicates directly with the Cloudflare API and purges all the relevant URLs.
  • Ghost is open source and the "Site changed" webhook could be updated so it sends data about what changed. Then the Cloudflare worker could identify what needs to be purged.
  • Any updates to CSS or JavaScript files will require a manual purge, a separate server process that watches for file changes could trigger the Cloudflare worker to purge the cache or call the API method directly. It wouldn't be too hard to implement in Node, and Ghost is already running on Node.