Skip to content

Instantly share code, notes, and snippets.

@diboune
Created January 23, 2026 08:44
Show Gist options
  • Select an option

  • Save diboune/efa759718f4da7c6804aea03367d0225 to your computer and use it in GitHub Desktop.

Select an option

Save diboune/efa759718f4da7c6804aea03367d0225 to your computer and use it in GitHub Desktop.
Astro + Sanity + Cloudflare Cache Tags

Astro + Sanity + Cloudflare Cache Tags

A guide to implementing cache tags with Astro, Sanity CMS, and Cloudflare for efficient cache invalidation.

1. Astro page with cache tags

---
// src/pages/blog/[slug].astro
import { sanityClient } from '../../lib/sanity';

const { slug } = Astro.params;

const post = await sanityClient.fetch(
  `*[_type == "post" && slug.current == $slug][0]`,
  { slug }
);

if (!post) return Astro.redirect('/404');

// Set cache tags: type + specific document
Astro.response.headers.set('Cache-Control', 'public, max-age=31536000');
Astro.response.headers.set('Cache-Tag', `post, post-${post._id}`);
---

<html>
  <body>
    <h1>{post.title}</h1>
    <div set:html={post.body} />
  </body>
</html>

2. Webhook endpoint

// src/pages/api/revalidate.ts
import type { APIRoute } from 'astro';

const ZONE_ID = import.meta.env.CF_ZONE_ID;
const CF_API_TOKEN = import.meta.env.CF_API_TOKEN;
const SANITY_WEBHOOK_SECRET = import.meta.env.SANITY_WEBHOOK_SECRET;

export const POST: APIRoute = async ({ request }) => {
  // Verify webhook secret
  const signature = request.headers.get('sanity-webhook-signature');
  if (signature !== SANITY_WEBHOOK_SECRET) {
    return new Response('Unauthorized', { status: 401 });
  }

  const body = await request.json();
  const { _type, _id } = body;

  // Build tags to purge
  const tags = [_type, `${_type}-${_id}`];

  // Purge Cloudflare cache
  const res = await fetch(
    `https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${CF_API_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ tags }),
    }
  );

  if (!res.ok) {
    return new Response('Purge failed', { status: 500 });
  }

  return new Response(JSON.stringify({ purged: tags }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' },
  });
};

3. Sanity webhook setup

Go to sanity.io/manage → Project → API → Webhooks → Add:

Field Value
URL https://yoursite.com/api/revalidate
Trigger Create, Update, Delete
Filter _type == "post" (or leave blank for all)
Projection {_type, _id}
Secret Generate one, add to .env as SANITY_WEBHOOK_SECRET

4. Environment variables

CF_ZONE_ID=your_zone_id
CF_API_TOKEN=your_api_token
SANITY_WEBHOOK_SECRET=your_secret

How to get Cloudflare credentials

  • ZONE_ID: Cloudflare dashboard → your site → Overview → right sidebar
  • CF_API_TOKEN: Cloudflare dashboard → My Profile → API Tokens → Create Token → "Purge Cache" permission

Flow

  1. User requests /blog/my-post
  2. Astro fetches from Sanity, renders page with Cache-Tag: post, post-abc123
  3. Cloudflare caches at edge
  4. Editor updates post in Sanity
  5. Sanity webhook fires to /api/revalidate with {_type: "post", _id: "abc123"}
  6. Endpoint purges tags ["post", "post-abc123"]
  7. Next request gets fresh content
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment