Docs
Concepts

Caching

How published reads stay in the CDN cache, what a publish purges, and what your own site may still hold.

View as Markdown

Every published read goes through Capa's CDN at https://cdn.capacms.com. The CDN keeps the response, and Capa purges it when the content behind it changes.

You do not configure this. You only choose which key you read with.

Which reads are cached

The key's environment decides. A production key reads published content, so its responses can be shared. A development key reads drafts, which have no business in a shared cache.

ResponseProduction keyDevelopment key
GET /api/entries/..., 200CachedNever cached
GET /api/graphql, no errorsCachedNever cached
POST /api/graphqlNever cachedNever cached
GET /v2/api/... and /v3/api/..., 200CachedNever cached
GET /v2/seo/..., 200CachedNever cached
GET /files/..., which takes no keyCached for a yearCached for a year
Any errorNot cached, with one exception belowNot cached

The exception: a production key's 404 entry_not_found for a well-formed id is cached like a 200. When an entry is taken down, the edge serves the 404 in its place, and the publish that brings it back purges the 404.

A GraphQL read that selects me, __schema or __type is never cached.

The headers

A production key's 200 on /api/ carries:

HeaderValue
Cache-Controlpublic, max-age=60
Surrogate-Controlthe edge lifetime, with stale windows
Surrogate-Keythe keys a purge targets (below)
ETaga strong tag over the body

A development key's response carries Cache-Control: no-store, no-cache and no ETag.

Cache-Control: max-age=60 is for your side: a browser or your own server may reuse the response for 60 seconds. The edge keeps it longer and relies on purges instead.

What a publish purges

Each cached response is tagged with surrogate keys. A change purges the keys it touches, so only the affected responses are dropped.

KeyOne per
t:<projectId>response
c:<projectId>:<contract>response
k:<keyId>response
m:<modelId>the root model, every expanded relation's model, and every model a filter or sort reaches
e:<entryId>entry in the response, and every expanded entry
f:<fileId>file a media value renders
ChangeWhat it purges
Publish an entryits e: key and its model's m: key, so every response showing it and every list of its model
Move an entry to another folderthe same keys
Unpublish or delete an entrythe same keys, hard
Delete a modelits m: key, hard
Delete a projectits t: key, hard
Edit a file's alt textits f: key
Delete a fileits f: key, hard
Deactivate, rotate, narrow or expire a keyits k: key

A publish marks the old copies stale. The edge can serve the stale copy once more while it fetches the new one, so a read in the same moment as the publish may still see the old body. A hard purge drops the old copy at once: unpublish and delete use it so taken-down content never lingers.

A response with too many entries to list keeps its t:, c:, k:, m: and f: keys and drops the e: keys. It then carries Capa-Cache-Scope: model. A purge is broader than it needed to be, never narrower.

Revalidate with an ETag

Send the ETag back as If-None-Match. An unchanged response is a 304 with an empty body.

curl -sD - -o /dev/null 'https://cdn.capacms.com/api/entries/articles?limit=2' \
  -H "x-api-key: $CAPA_KEY" -H 'If-None-Match: "3f9c1a…"'

The tag covers data and page, never meta, because meta.requestId changes on every request.

Rate limits

Capa does not limit how many requests a key makes per minute. It limits how many reads run at once, so a build that sends many reads in parallel can get 429 rate_limit_exceeded with a Retry-After in seconds. Send fewer at a time, and retry after that many seconds.

The legacy API

/v2/api and /v3/api cache the same way for a pk_ key: public, max-age=60 for your side, a longer edge lifetime, and a purge of every cached copy of the model when one of its entries is published. An sk_ key is never cached.

After a publish, allow for the 60 seconds your side may still hold.

Files and images

A file under /files/ is cached for a year, at the edge and in the browser. Every resized or converted variant carries the file's own key, so deleting the file purges every variant at once. Replacing a file does not purge the edge, so the old copy can stay for up to a year. Upload a new file when a change must show. See Images.

Your own caches

The CDN is Capa's half. Anything your site keeps on top is yours to expire:

  • Your server or framework. Next.js, for one, can keep fetch results in its data cache. See Next.js for tags and webhook revalidation.
  • A CDN of your own. The SDK gives you the response's surrogate keys as cacheTags, so you can purge by the same keys.
  • The browser. max-age=60 lets it reuse a response for a minute.