DevKitHub

API & Security

Cache-Control Header Analyzer and Explainer

Paste a Cache-Control value to get a plain sentence for what a browser will do and what a shared cache will do, plus any directives that contradict each other.

What will actually happen

Browser
Reuses it for 365 days without contacting the origin, and will not revalidate even on a reload.
Shared cache
Reuses it for 365 days, the same as the browser.

Directives

public
A shared cache may store it even if the request was authenticated.
max-age31536000
Seconds a response stays fresh. After this it must be revalidated.
immutable
The body will never change while fresh, so skip revalidation on reload.

Nothing flagged.

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

The header is short, looks self-explanatory, and is routinely wrong. The single biggest reason is that no-cache does not mean "do not cache" — it means "store this, but revalidate before reusing it". The directive that prevents storage is no-store. People reach for no-cache intending no-store, find the response stored, and conclude that caching is broken. This tool leads with the behaviour rather than the directive list for exactly that reason.

The other half of the confusion is which directives address whom. private and s-maxage both concern shared caches, in opposite directions, and setting both is a contradiction no cache will report back to you: s-maxage tells a CDN how long to keep the response and private tells it not to keep it at all. public alongside private is the same kind of mistake made louder. Each of those is detected and explained rather than left to be discovered from a cache-hit ratio.

The output is two sentences, one for the browser and one for a shared cache, because that is the question behind the header. Where a duration is involved it is rendered in human terms, so max-age=31536000 reads as a year, and the combinations that do not make sense together — immutable with no max-age, stale-while-revalidate with nothing to make the response fresh in the first place — are named with what to do instead.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

no-cache was set and the response is still being stored.

Why:
That is what no-cache means. It permits storage and requires revalidation before each reuse. The directive that prevents storage entirely is no-store.
Fix:
Use no-store for a response that must never be written to disk, such as one containing personal data.

The CDN is not caching despite s-maxage being set.

Why:
private is also set. private tells shared caches not to store the response at all, which makes s-maxage — a directive addressed only to shared caches — dead.
Fix:
Remove private if a CDN is meant to cache this, or remove s-maxage if it is not.

Assets are re-downloaded on every reload despite a long max-age.

Why:
A reload revalidates regardless of freshness unless immutable is present. Without it the browser asks every time, even though the answer is always 304.
Fix:
Add immutable alongside a long max-age, for URLs that contain a content hash and therefore never change.

Frequently asked questions

What is the difference between no-cache and no-store?
no-cache allows the response to be stored and requires revalidation before each reuse. no-store forbids writing it anywhere at all. Almost everyone who writes no-cache means no-store.
When should I use immutable?
On URLs that contain a content hash, alongside a max-age of about a year. It tells the browser not to revalidate even on a reload, which is the one case a long max-age alone does not cover.
Does s-maxage override max-age?
For shared caches only — a CDN uses s-maxage and the browser uses max-age. That is what makes the pair useful: a short browser lifetime with a long CDN lifetime, purged on deploy.

Read more about this

Last updated