Tag-based caching with DatoCMS & Astro.
Canner can cache your server-rendered responses and invalidate them by tag. When you publish in your CMS, a webhook clears exactly the routes that changed — so you serve from cache between edits instead of hitting your CMS API on every page view.
Caching applies to server-rendered output. For Astro that means the Node adapter (@astrojs/node). A fully static Astro build hits your CMS only at build time, so it doesn’t need per-request caching — redeploy to refresh it.
1. Turn caching on
In your project’s Settings → Caching, flip Response caching on (paid plans). This adds Canner’s cache in front of your app. Nothing is cached until your responses ask for it — see step 2.
2. Mark responses cacheable & tag them
On the routes you want cached, set a Cache-Control header withs-maxage, and tag the response with one Surrogate-Key per entity that appears on the page. Tags are scoped to your project automatically, so just use plain, readable names:
---
// src/pages/blog/[slug].astro (output: 'server')
const post = await getPost(Astro.params.slug);
// Cache this response on Canner for an hour (until a purge or expiry).
Astro.response.headers.set(
"Cache-Control",
"public, s-maxage=3600"
);
Astro.response.headers.set(
"Surrogate-Key",
[`post-${post.id}`, "blog-listing"].join(" ")
);
---A single entity can appear on many routes (post page, listing, homepage). Tagging each response with the entity’s key lets one purge clear all of them at once. After a purge the next visitor to a cleared route triggers one fresh render, which is then cached again.
Prefer one line? Install @canner-ca/astro-cache and it sets both headers correctly — including the easy-to-forget public — and coerces numeric DatoCMS ids for you:
---
import { cache } from '@canner-ca/astro-cache';
const post = await getPost(Astro.params.slug);
cache(Astro.response, { ttl: 3600, tags: [post.id, 'blog-listing'] });
---Using Next.js? Server components can’t set response headers, so cache App Router pages from middleware.ts (or use Route Handlers /getServerSideProps). The @canner-ca/next-cache helper covers all three. Canner caches the HTML document and passes client-side RSC navigations straight through, so a cached page is never served stale to a soft navigation.
Using Nuxt? Use @canner-ca/nuxt-cache in a page (cache(useRequestEvent(), …)), a server route, or middleware. Nuxt fetches payloads from separate URLs, so pages cache cleanly; Canner passes any x-nuxt-no-ssr request through so the SPA shell is never cached.
3. Point your CMS webhook at Canner
In Settings → Caching, generate a Purge token (shown once). In DatoCMS, add a webhook that fires on publish/unpublish and sends:
- URL:
https://api.canner.ca/projects/<your-slug>/cache/purge - Method:
POST - Header:
Authorization: Bearer <your purge token>
Canner reads the changed record from the DatoCMS payload automatically. You can also send an explicit body to purge specific tags:
curl -X POST https://api.canner.ca/projects/<your-slug>/cache/purge \
-H "Authorization: Bearer <your purge token>" \
-H "Content-Type: application/json" \
-d '{"tags": ["post-123", "blog-listing"]}'Send the same tag names your app emitted in Surrogate-Key. Canner evicts every cached response carrying any of those tags — scoped to your project, so your tags can never affect another site even if the names collide.
Purge everything
Need a clean slate? The Purge everything button in Settings → Caching clears the whole project cache. Useful after a redeploy that changed layout or shared components.
Static sites: rebuild instead of purge
If your Astro site is fully static (no @astrojs/node), there’s no per-request cache to purge — the pages were rendered at build time. For those, point your CMS webhook at the rebuild endpoint instead, using the same purge token. It redeploys your project from the connected GitHub repo:
curl -X POST https://api.canner.ca/projects/<your-slug>/cache/rebuild \ -H "Authorization: Bearer <your purge token>"
Rebuild needs a GitHub-connected project (it builds from your repo’s HEAD). SSR sites should use /cache/purge above — it’s instant and doesn’t cost a build.
Good to know
- Only responses with
Cache-Control: publicand a positives-maxageare cached. Authenticated orSet-Cookieresponses are never cached. - Purges are scoped to your project — you can never affect another tenant’s cache, and no one can purge yours without your token.
- Entries also expire on their own once
s-maxageelapses; purging is just the fast path when content changes sooner.