In my previous post, I discussed integrating Bunny CDN to enhance my blog's performance. If you're interested, you can read about it in the post below:

Integrate Bunny CDN with Ghost
Learn how to boost your Ghost blog’s performance by using BunnyCDN. Discover step-by-step configuration tips, caching strategies, and edge rules setup to achieve better cache hit ratios, faster loading times, and improved global content delivery.

While Bunny CDN improved performance, I encountered a significant workflow issue: manually purging the cache after publishing each post. This manual step required logging into Bunny CDN's website, and like any manual process, I sometimes forgot to do it. When that happened, visitors would see outdated cached content instead of my new posts. To eliminate this overhead and potential for human error, I decided to automate the cache purging process.

During my research on Ghost CMS's documentation, I discovered a valuable feature: webhooks that can trigger cache purging automatically whenever a site.changed event occurs.

Ghost Webhooks
Trigger events when something happens in Ghost using webhooks configured through the Ghost Admin user interface. Find out more on Ghost Docs 👉

This feature significantly simplified my workflow. Setting it up only requires creating a custom integration through the admin portal. With the trigger mechanism built in, I could focus entirely on developing an always-online middleware to handle the cache purging process.

Workflow Design

Since the process would now be automated, the middleware needed to interface with Bunny CDN's API to handle cache purging. After consulting the official API documentation, I found that purging the entire Pull Zone's cache was straightforward:

Purge Cache

All I need are two parameters: My Bunny account AccessKey and The Pull Zone ID.

With the API requirements clear, the next step was choosing an appropriate development platform and language for the middleware. Cloudflare Workers emerged as the ideal solution, offering a serverless execution environment where I could develop applications without managing infrastructure. I chose JavaScript for its straightforward deployment process to Cloudflare Workers.

Another notable issue which needs to be solved is Bunny CDN is Perma-Cache, a secondary permanent cache layer between the CDN and your origin. When content isn't found in the CDN cache, it checks the Geo-Replicated storage before reaching the origin server.

While this caching system enhances performance, it has a potential drawback: each cache purge creates a new folder in the linked Storage Zone without removing previous ones. This accumulation could lead to unnecessary storage costs over time. To address this, the workflow needs to incorporate Storage Zone API calls to identify and remove outdated Perma-Cache folders. The API documentation related is as below:

Delete File
Delete an object from the storage zone. In case the object is a directory all the data in it will be recursively deleted as well.

The complete cache purging automation workflow can be visualized in the flowchart below:

Source Code Explanation

I have open sourced the code on GitHub, which you can find in the below repo:

GitHub - MFYDev/Ghost-BunnyCDN-Cache-Clearer: Cloudflare Worker that automatically purges BunnyCDN pull zone cache and cleans up Perma-Cache folders upon receiving Ghost CMS webhooks, optimizing storage usage and reducing costs.
Cloudflare Worker that automatically purges BunnyCDN pull zone cache and cleans up Perma-Cache folders upon receiving Ghost CMS webhooks, optimizing storage usage and reducing costs. - MFYDev/Ghost…

Purpose of the Code

  • Automates cache purging in BunnyCDN via API integration.
  • Verifies requests using a Ghost webhook signature or manual trigger token.
  • Cleans up unused folders in BunnyCDN’s Perma-Cache storage.

Key Features

  • Webhook Verification: Ensures only authorized requests (from Ghost or a manual trigger) are processed.
  • Pull Zone Cache Purge: Clears BunnyCDN’s pull zone cache for the specified zone ID.
  • Perma-Cache Cleanup: Identifies and deletes stale folders in BunnyCDN storage.

How the Code Works

  1. Request Handling
    • Listens for HTTP requests.
    • Validates required environment variables.
    • Routes the request based on the URL path (e.g., /purge-full-cache).
  2. Authentication
    • Manual Trigger: Checks for a token in the request headers.
    • Ghost Webhook: Verifies the signature using a secret key and HMAC.
  3. Pull Zone Cache Purge
    • Calls BunnyCDN API to purge the pull zone cache.
    • Logs success or failure.
  4. Perma-Cache Folder Cleanup
    • Fetches a list of folders in the Perma-Cache directory.
    • Deletes each folder and logs the results (success or failure).
  5. Error Handling
    • Logs all errors with detailed messages.
    • Responds with appropriate HTTP status codes.

Required Environment Variables

  • GHOST_WEBHOOK_SECRET: Secret key for verifying Ghost webhook requests.
  • BUNNY_PULLZONE_ID: ID of the BunnyCDN pull zone.
  • BUNNY_API_KEY: API key for BunnyCDN access, where you can get yours here.
  • BUNNY_STORAGE_ZONE_HOSTNAME, BUNNY_STORAGE_ZONE_NAME, BUNNY_STORAGE_ZONE_PASSWORD: Credentials for BunnyCDN storage, where you can get in the FTP & API Access tab in your storage zone menu.

Workflow Summary

  1. Trigger: A request to /purge-full-cache.
  2. Validate: Environment variables and authentication.
  3. Purge: Clear pull zone cache via BunnyCDN API.
  4. Clean: Delete stale folders from Perma-Cache.
  5. Respond: Return a summary of actions performed.

Code Deployment

Deploy to the Cloudflare Worker

Deployment to Cloudflare is straightforward: navigate to the Workers & Pages tab in your Cloudflare dashboard, create a new worker, and paste the code from worker.js.

Then, configure your environment variables by accessing Settings → Variables and Secrets in your worker's configuration panel and input your credentials.

Create Custom Integration in Ghost

Cloudflare automatically assigns a public domain to each worker. To connect it with Ghost, create a custom integration under the Integrations tab as shown below:

That's it! Now whenever your website changes, it will automatically trigger the Cloudflare Worker to clear all caches.

Security & Manual Trigger

The code incorporates multiple layers of security to prevent unauthorized access. For requests from Ghost, it validates a webhook signature using an HMAC mechanism with a shared secret (GHOST_WEBHOOK_SECRET). This ensures that only requests generated by Ghost are accepted. The signature validation also includes a timestamp check, rejecting requests older than 5 minutes to guard against replay attacks.

For administrative purposes, the code supports a manual trigger mechanism. By including a pre-defined manualtriggertoken in the HTTP request headers, authorized users can bypass signature verification. However, this token is securely stored as an environment variable (MANUAL_TRIGGER_TOKEN), ensuring only those with access to the correct token can execute the manual trigger. Together, these measures safeguard the integrity of the workflow while allowing flexibility for cache management tasks.

Conclusion

By automating the cache purging process with Ghost webhooks and Cloudflare Workers, I've eliminated manual intervention and potential human error from my blog's content delivery workflow.

The solution not only handles Bunny CDN's cache purging automatically but also manages Perma-Cache storage efficiently, preventing unnecessary costs. This automated system ensures that my readers always see the latest content while maintaining optimal performance.

The entire solution is open-source and readily available for other Ghost CMS users who want to implement similar automation in their BunnyCDN setup.