Digitized picture of a rusty satellite spray-painted with the Cloudflare logo in Earth orbit.
Taking CloudFlare to the Moon & Back!Taking CloudFlare to the Moon & Back!

Cloudflare Advanced - The Layered Cache Strategy (2026)

Learn how to utilize Cloudflare's FREE tier to perform advanced & powerful layered caching to give your services sub second load times!

In Part 1, I secured my DNS and dynamic homelab IP with OPNsense. In Part 2, I configured Cloudflare's free edge security, WAF geoblocking, and bot defence. Now, in Part 3, I unlock the true speed of the Edge: Intelligent Layered Caching.

This is the third post in the Cloudflare series, showcasing utilizing your domain with their free tier. if you missed the first two posts, click back with the series navigator below.

Series Roadmap • Part 3 of 4
Cluster: Edge Security & Ingress

The Cloudflare Edge & Perimeter Shield Series

A battle-tested blueprint for configuring Cloudflare DNS, securing dynamic home server IPs, hardening edge WAF rules against malicious bots, and tuning caching without breaking application authentication.

Tested on bare-metal production topology
Core Lab Edge Stack · Cloudflare + OPNsense DDNS + Reverse Proxy
Crucial Architecture Update • Edge Caching & Purge Overhaul

Disclaimer: Some of the original cache bypass rules and the naive purge_everything: true worker script originally documented in this post were overly aggressive. Depending on your backend CMS, authentication headers, and dynamic cookies, aggressive edge caching can silently intercept and break member login sessions, newsletter signups, and magic-link validation.

I suffered this exact production breakdown on my own live Ghost CMS deployment. This post has now been completely overhauled below with modernized Cloudflare Cache Rules, precise auth cookie bypasses, and surgical cache purging. Before deploying edge caching on any dynamic CMS, read the complete autopsy in Part 4: The Post-Mortem to avoid repeating my mistake.


Most guides tell you to "Cache Everything" and hope for the best. That breaks your site for logged-in members. Others tell you to create complex bypass rules first.

If you are self-hosting a Ghost blog (or any CMS/website), your server is in one physical location (e.g., your basement/rack/IKEA Lack table!). When a user in London visits, they have to wait for light to travel across the Atlantic ocean, which, is big! Thankfully fibre is quite fast, but there is a delay, and when your site gets busier there becomes contention for that data.

We are going to use a Layered Strategy. We set a "base layer" of caching for the whole site, and then we layer specific "overrides" on top of it for members and static files. This configuration is currently running on Core Lab, keeping the site instant for guests while ensuring members never see stale data. I may have to revisit this logic if the site grows much larger, but when we cross that bridge... One can hope! ;)

We can cheat physics using Cloudflare’s Edge Cache.

The goal of this guide is to make your dynamic content feel like a static site and react that quickly. We will force Cloudflare to cache everything (including the HTML pages that it normally does not) and then use a Cloudflare Worker to automatically clear that cache the instant you publish a post!

As I always say, a picture speaks a thousand words:
flowchart TD
    %% ===========================
    %% CLOUDFLARE LAYERED CACHE STRATEGY
    %% ===========================

    subgraph Base ["🌍 Rule #1 - Base Layer
Cache Everything (HTML) for 7 Days"] R1["🟦 7-Day Cached HTML
• Speeds up guest traffic
• Default cache state for whole site"] end subgraph Override ["🛑 Rule #2 - Override
Bypass Cache for Members & Admin"] R2["🔴 Cache Bypass
• Logged-in users
• Admin dashboard
• Dynamic pages (cart/checkout/etc)"] end subgraph Boost ["🚀 Rule #3 - Static Asset Boost
Cache for 1 Month"] R3["🟨 30-Day Cache
• Images
• CSS/JS
• Fonts (woff2)
• Media assets"] end R1 --> R2 --> R3 style Base fill:#e0f2fe,stroke:#0284c7,stroke-width:2px,color:#034168 style Override fill:#fee2e2,stroke:#dc2626,stroke-width:2px,color:#7f1d1d style Boost fill:#fef9c3,stroke:#ca8a04,stroke-width:2px,color:#7c5800 style R1 fill:#fff,stroke:#0284c7,stroke-width:2px style R2 fill:#fff,stroke:#dc2626,stroke-width:2px style R3 fill:#fff,stroke:#ca8a04,stroke-width:2px

Prerequisites:

  • A Cloudflare account (Free Plan).
  • A Ghost blog / website / something online (Self-hosted).
  • Access to your Cloudflare Dashboard and Admin panel.
    • For this example, I'll be showing how it's done for my blog here which is based on Ghost but the concepts should be the same for other sites.

🧠 The Concept: Static vs. Dynamic Caching

Before we click buttons, you need to understand the strategy.

  1. Static Content (Images, CSS, JS): Cloudflare caches these by default. You don't need to do anything.
  2. Dynamic Content (HTML): By default, Cloudflare does NOT cache the actual HTML pages of your blog. It fetches them from your server every single time. Not what you expected eh? Me neither!
The Strategy: We are going to force Cloudflare to cache the HTML too. This reduces the load on your server to near-zero and makes your site load instantly worldwide.

⚠️ The Warning (Ghost Members):

If you use the "Members/Login" feature of Ghost, aggressive HTML caching can break the "Logged in as..." functionality. I will show you a "Pro Tip" in Step 2 to handle this using modern Cache Rules.


Fixing My Flaw

💥
I had a flaw in the original version of this guide!!! Fixed as of October 2026.

Flaw 1: The Singular vs. Plural Typo

In my Cloudflare Cache Rule expression, I had created a rule to bypass dynamic paths. In that expression was this condition:

http.request.uri.path contains "/member/"

Notice the trailing singular slash: /member/. Ghost's active session endpoint is singular (/members/api/member/), but its authentication, integrity, and SSR endpoints are strictly plural: /members/api/integrity-token/, /members/api/send-magic-link/, and /members/ssr/.

Because /members/api/integrity-token/ did not match /member/, the request fell through my bypass filter and was captured by my broad caching rules.


🛠️ Part 1: Setting Up Edge Caching (The Modern Way)

We are going to use Cache Rules, which are the modern, more powerful replacement for the old "Page Rules."

The Core Lab Strategy: "Last Match Wins"

We will configure 3 Cache Rules in specific order. Cloudflare processes these top-to-bottom, meaning the last rule that matches is the one that applies.

  1. 🌍 Rule #1 (The Base): Cache Everything for 7 Days. (This sets the default state).
  2. 🛑 Rule #2 (The Override): Bypass Cache for Members & Admins. (This overwrites Rule #1 for logged-in users).
  3. 🚀 Rule #3 (The Boost): Cache Static Assets for 1 Month. (This overwrites Rule #1 for images/css, giving them an even longer life).

Navigate to your Domain->Rules->Cache Rules in your Cloudflare Dashboard.

Screenshot of my 3 Cloudflare Cache rules, live, as they are today.
My 2 Cloudflare Cache rules, live, as they are today. Rule #3 was removed as of Oct 2026.

Create these rules in this exact order, the order absolutely matters.

Rule #1: The Base Layer (7 Days HTML)

First, we cast a wide net. We tell Cloudflare: "Unless I tell you otherwise later, cache everything on this domain for a week."

  • Rule Name: Cache Public Content (7 Days)
  • For If incoming requests match...
    • Chose Custom filter expression
  • Expression:
    • Hostname equals corelab.tech (Swap with YOUR domain of course)
  • Cache Status: Eligible for cache
  • Edge Cache TTL: Ignore cache-control header -> 7 Days
  • Browser TTL (optional)
    • I set mine to Override origin and use this TTL and set that to 4 hours.
The Effect: Right now, everyone (including you) sees a cached version of the site. This makes it fast, but we need to fix the "logged in" experience next.
Screenshot showing CF 7 day cache rule settings
CF 7 day cache rule settings

Rule #2: The Bypass (Members, Admin & SEO)

Now, we apply the critical overrides. We need to tell Cloudflare to stop caching immediately if a user is logged in (dynamic data) or if a search engine is looking for your site structure. This one will take a beat longer than the first one.

Rule Name: Bypass Cache for MbrsAdmins (or whatever makes sense to you)

📡
I have updated this rule & these instructions on May 1 2026. If you previously implemented this rule, have a 2nd look and re-work your rules if things aren't working!
😮
Very easy to mess this rule up, pay close attention to the order and where the AND and OR logic chains are! Take a 3rd look at the pic 😉
Screenshot showing the complex rule for members & admins bypassing cache, but it's complex. Use the expression builder below.
This is why using the expression builder is easier and faster than manually clicking through all of this!

So there's 2 of those And sets of rules, then the rest have Or in between them.

Expression code block which you can copy & paste into the Expression builder:

(http.host eq "corelab.tech" and http.cookie contains "ghost-members-ssr") or (http.host eq "corelab.tech" and http.cookie contains "ghost-admin-api-session") or (http.request.uri.path contains "/ghost/") or (http.request.uri.path contains "/p/") or (http.request.uri.path contains "/members/") or (http.request.uri.path contains "sitemap") or (http.request.uri.path contains ".xsl") or (starts_with(http.request.uri.path, "/.ghost/activitypub/")) or (starts_with(http.request.uri.path, "/.well-known/"))

Once you paste this in and click to Use Expression Builder the rules above are converted to a more easily modifiable human format as the picture shows.

Cache Status: Bypass cache

The Logic: Because this rule comes after Rule #1 (the "Cache Everything" rule), rule #1 takes precedence first.

The Effects:

For Users: If Cloudflare detects a "Login Cookie," it discards the cache and fetches live data so your members never see a generic cached page.
For Google: By adding sitemap and .xsl here, we ensure that every time you publish a post, Googlebot sees the new link instantly - no waiting 7 days for the cache to clear.

🕵️ Verification: How to Test

It is critical to verify that your "layering" is working.

1. Test as a Guest (Incognito Window): Visit your homepage. Check the Network tab (F12) for the main document request.

  • Header: cf-cache-status: HIT (or MISS then HIT on refresh)
  • Explanation: You matched Rule #1.

2. Test as a Member (Logged In): Log in to your site. Check the same request.

  • Header: cf-cache-status: BYPASS
  • Explanation: You matched Rule #1, but then you matched Rule #2, which overwrote it to "Bypass."

3. Test an Image: Right-click an image and open it in a new tab. Check the headers.

  • Header: cache-control: max-age=2592000 (30 Days)
  • Explanation: You matched Rule #1 (7 days), but then matched Rule #3, which upgraded it to 1 Month.

⚙️ Part 2: The Automation (Cloudflare Workers)

If you stopped now, your blog would be fast, but stale. New posts wouldn't appear for a month! You could simply do a full purge every time you post, but that is just another thing you have to do... In 2026, we automate. We need a robot to tell Cloudflare: "I just published! Clear the cache!"

We will use a Cloudflare Worker (Free) triggered by a Ghost Webhook.

Step 1: Create a Cloudflare API Token

The Worker needs permission to purge your cache.

  1. Go to My Profile > API Tokens.
  2. Click Create Token.
  3. Use the "Custom Token" template.
  4. Permissions:
    • Zone -> Cache Purge -> Purge
  5. Zone Resources:
    • Include -> Specific zone -> corelab.tech (you'd use YOUR domain here)
  6. Create the token and COPY IT. You will not see it again.

Step 2: Create the Worker (Crazy this is free!)

  1. Go to Compute & AI -> Workers tab in the sidebar.
  2. Click Create Service.
  3. Name it: ghost-purge or autopurger. Select "HTTP handler" (standard).
  4. Click Create service.
  5. Click Edit code.
  6. Delete the existing code and paste this script. This script listens for the Ghost webhook and commands Cloudflare to purge the specific URL that was updated.
worker.js · Surgical Cloudflare Cache Purger
ES Module (v3)
export default {
  async fetch(request, env) {
    if (request.method !== "POST") {
      return new Response("Method Not Allowed", { status: 405 });
    }

    const url = new URL(request.url);
    const secret = url.searchParams.get("secret");

    // Secure authentication token check
    if (!env.PURGE_SECRET || secret !== env.PURGE_SECRET) {
      return new Response("Unauthorized", { status: 401 });
    }

    let payload;
    try {
      payload = await request.json();
    } catch (e) {
      return new Response("Invalid JSON payload", { status: 400 });
    }

    // Extract updated post URL from Ghost webhook payload
    const postUrl = payload.post?.current?.url;
    const homeUrl = "https://corelab.tech/";
    const rssUrl = "https://corelab.tech/rss/";

    const purgeFiles = [homeUrl, rssUrl];
    if (postUrl) purgeFiles.push(postUrl);

    // Also purge primary tag page if present
    const primaryTag = payload.post?.current?.primary_tag?.slug;
    if (primaryTag) {
      purgeFiles.push(`${homeUrl}tag/${primaryTag}/`);
    }

    // Surgical Cache Purge API Call
    const zoneId = env.ZONE_ID || "YOUR_CLOUDFLARE_ZONE_ID";
    const cfResponse = await fetch(
      `https://api.cloudflare.com/client/v4/zones/${zoneId}/purge_cache`,
      {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${env.API_TOKEN}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ files: purgeFiles })
      }
    );

    const result = await cfResponse.json();
    return new Response(JSON.stringify({ success: result.success, purged: purgeFiles }), {
      headers: { "Content-Type": "application/json" }
    });
  }
};
Target: Scoped Cache Purge only (No Global API Key) Zone Route: corelab.tech/.ghost-cache-purge*

🔗 Part 3: Connecting Ghost

The final step is telling Ghost to talk to your Worker.

  1. Log in to your Ghost Admin.
  2. Go to Settings (Gear) > Integrations.
  3. Click Add custom integration (bottom of page).
  4. Name it "Cloudflare Purge".
  5. In the Webhooks section, click Add webhook.
    1. There's two methodologies you can go with here, one webhook, or two separate. I chose simplest, using one.
  6. Option A - Create one webhook:
    1. Event: Site changed (rebuild) | Target URL: Your Worker URL.
      1. This means that if ANY change is made to any post on the site, the entire cache on CloudFlare is wiped and CloudFlare then has to re-cache & crawl the site. "Nuke from orbit".
  7. Option B - Create two webhooks:
    • Event: Post published | Target URL: Your Worker URL.
    • Event: Post updated | Target URL: Your Worker URL.
      • This is more granular and keeps a good chunk of your static content still cached.
  8. Click Save.

🚀 The Result & Cache Score

  1. You publish a post.
  2. Ghost fires a signal to your Cloudflare Worker.
  3. The Worker tells Cloudflare, "Clear the cache for this specific URL."
  4. Global Cache: Your readers serve the super-fast cached version (HIT) from the edge.
  5. Zero Staleness: The moment you fix a typo, the cache clears instantly.

When done, after about a week (depending how busy your site is!) you should see some incredible statistics like this -

Cloudflare Dashboard showing very close to 90% cache hit status and 90% cached data served!
Cloudflare Dashboard showing very close to 90% cache hit status and 90% cached data served!

You now have the best of both worlds: the dynamic power of Ghost and the static speed of the Cloudflare Edge, all for $0/month.


This is a part of my practical cybersecurity roadmap for homelabs/self-hosters, hop over there to begin your next adventure!

A Practical Cybersecurity Roadmap for Homelabs
Learn to protect your data, services and privacy with actionable steps and clear tutorials. So, you’ve built an incredible homelab. You’re spinning up services in Docker, managing your media, and maybe even self-hosting your own website. You are the master of your own data. But with every new service you

If you are enjoying and getting use out of this post or the site, feel free to sign up or donate, this would be hugely appreciated by me and signals to me you value the content and want more!👇

Core Lab
Core Lab: Building Your Digital Fortress. Explore homelabbing, smart homes & secure self-hosting. Join our community and build your homelab today!

❓ Frequently Asked Questions (FAQ)

Why does Cache Rule order matter in Cloudflare?

Cloudflare processes cache rules top-to-bottom, and the last rule that matches a request is the one that actually applies. That's why the "Cache Everything" base rule has to come first, the bypass rule for logged-in members/admins comes second to override it for those users, and the static-assets rule comes last to give images and CSS an even longer cache life than regular pages.

Won't caching my whole site mean visitors see stale content?

Not if you automate purging. Caching everything for 7 days makes your site fast, but new posts wouldn't show up until the cache expires without a purge mechanism. The fix is a Cloudflare Worker triggered by a Ghost webhook that automatically purges just the affected URL the moment you publish, so the site stays both fast and fresh.

Do logged-in members see a cached version of the site?

No - that's exactly what the bypass rule is for. Members and admins are excluded from the cache-everything rule, so they always see live, uncached content, while anonymous visitors get served the fast cached version.

What permissions does the Cloudflare API token need for auto-purging?

Just one: Zone → Cache Purge → Purge, scoped to the specific zone (your domain). You don't need broader account-level permissions - a narrowly scoped token limits the blast radius if it's ever leaked.

Is this layered caching setup free to run?

Yes. Cache Rules and Cloudflare Workers (used here for the auto-purge automation) are both available on Cloudflare's free tier - no paid plan is required for this entire setup.

Share this article
Share

Written by

Core Lab Joe
Core Lab Joe
Dad, IT guy, Self-Hosting Evangelist! https://www.reddit.com/r/corelabtech/
corelab.tech
Facebook
Mastodon

On this page

What's Next?