A guide to implementing cache tags with Astro, Sanity CMS, and Cloudflare for efficient cache invalidation.
---
// 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>// 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' },
});
};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 |
CF_ZONE_ID=your_zone_id
CF_API_TOKEN=your_api_token
SANITY_WEBHOOK_SECRET=your_secret- ZONE_ID: Cloudflare dashboard → your site → Overview → right sidebar
- CF_API_TOKEN: Cloudflare dashboard → My Profile → API Tokens → Create Token → "Purge Cache" permission
- User requests
/blog/my-post - Astro fetches from Sanity, renders page with
Cache-Tag: post, post-abc123 - Cloudflare caches at edge
- Editor updates post in Sanity
- Sanity webhook fires to
/api/revalidatewith{_type: "post", _id: "abc123"} - Endpoint purges tags
["post", "post-abc123"] - Next request gets fresh content