Caching
A CWA page can be served from several caches before it reaches the API. Each one has its own page in these docs. This page shows how they fit together, so you can follow one request through all of them.
The key idea: the API decides what may be cached, and the API purges it. Every cacheable response is tagged with the IRIs of the resources in it. When an editor saves a resource, the API sends that IRI as a purge, and every cached response that used the resource is dropped. So caches can keep responses for a long time and still never show stale content.
The layers
A visitor's request passes through these layers, outermost first. Only a miss goes on to the next one.
Browser ── service worker (cwa-api cache, read only when offline)
│
▼
Cloudflare (optional) ── edge cache, only with Option B
│
▼
Caddy in the php container ── Souin: page HTML and /_api responses
│ │
│ page miss │ /_api miss
▼ ▼
Nuxt (SSR) ───────▶ API (Symfony) ── sets Cache-Control and the cache tags
│ │
└── tags the page └── purges tags when a resource is written
with every IRI it used
DiagramCiPipeline. Left to right: browser and service worker, Cloudflare (dashed, optional), Caddy with Souin, then Nuxt SSR and the API side by side. Arrows for a request going in and a miss going on. A second colour for purges: the API sending tags to Souin, and Souin forwarding them to Cloudflare.What each layer does
| Layer | What it caches | How long | What clears it | Details |
|---|---|---|---|---|
| Browser HTTP cache | Nothing from CWA except built files. Pages and API responses send max-age=0 | Built /_nuxt files are immutable | A new build gives the files new names | Page Caching |
| Service worker | The app shell, and public API responses in a cwa-api cache, which is only read while offline | Up to 4 hours in the template | A response marked private or no-store is never stored. The module deletes cwa-api when a session ends | PWA & Offline |
| Cloudflare (optional) | Nothing with Option A, the recommended default. Page HTML with Option B | The origin's s-maxage | Souin forwards each tag purge to Cloudflare. A full flush sends Purge Everything | Cloudflare |
| Souin in Caddy | Page HTML that Nuxt rendered, and /_api responses | The response's s-maxage: one year in the template's production API, 60 seconds in development | A tag purge from the API, a full flush, or a php restart, because the template's store is in memory | The HTTP cache in Caddy |
| Nuxt page tagging | Nothing itself. It sets the headers that let Souin store a page | The lowest s-maxage of the API responses the page used, capped by pageCache.sharedMaxAge | Purged with the resources it used. Every page carries the cwa-html tag too | Page Caching |
| API cache tags | Nothing itself. It marks each response and sends purges | Shortened ahead of a scheduled publish or route go-live | Writing a resource purges its IRI. Saving site config purges cwa-html | HTTP Caching |
Signed-in editors never get a cached page
Draft and published content share the same URLs. The API tells them apart by the auth cookie, not the URL. So the API marks an editor's responses for routes, manifests, positions and publishable components private, no-store, and the module marks an editor's pages the same way. A cache only ever holds what an anonymous visitor sees. See HTTP Cache Safety.
pageCache.sharedMaxAge to cap how long such pages are kept. See How Long a Page Is Kept.Purges
| What happens | What is purged |
|---|---|
| An editor saves a resource | That resource's IRI, so every response and page that used it |
| An editor saves site config | cwa-html, so every rendered page |
| A scheduled publish or route go-live arrives | Nothing: the API shortened the lifetimes in advance, so those responses have already expired |
| A deploy | Every rendered page (cwa-html), or the whole cache when Cloudflare edge caching is set up. The pipeline then warms the cache |
Purge all cached data in site settings, purge-http-cache, or a fixture load | The whole HTTP cache |
purge-http-cache --path or --tag | One page, or any tag |
See Purging All Cached Data, Purging One Page and CI/CD → Why every deploy clears the page cache.
Protecting the origin
A cache only helps if made-up URLs can't get past it. The template's Caddyfile drops query parameters that pages don't read, strips tracking parameters, and rate-limits requests that miss the cache. On Kubernetes, ingress-nginx can add an outer rate limit, and php sheds load when every worker is busy. See Origin Protection.
Next: Glossary
The Glossary defines every CWA term used in these docs, including the caching ones.