Skip to content
AZ Tools

Cache-Control Header Builder

Build an HTTP Cache-Control header from a visual checklist — freshness, revalidation, immutability, and the common presets.

Presets
Visibility
Freshness (seconds)
Directives
Output
Cache-Control: public, max-age=3600

no-store overrides every other directive — when it's on, only no-transform is also emitted.

How to use

  1. Start from a preset that matches your scenario, then tweak.
  2. Numeric fields take seconds — leave them blank to omit the directive entirely.
  3. Copy the result into your origin server, CDN rule, or framework's response headers.

About this tool

Show

Composes a valid Cache-Control header following RFC 9111. Tick the directives you need (no-cache, no-store, immutable, …), set the freshness windows (max-age, s-maxage, stale-while-revalidate, stale-if-error), and copy the resulting header. Includes four presets covering the most common deployment patterns: immutable static asset, private rendered page, public API response with stale serving, and full no-store.

The directive people get wrong is no-cache, which does not mean do not cache. It means the response may be stored but must be revalidated with the origin before it is reused, so a cache still keeps a copy and a conditional request can still answer 304. The directive that actually forbids storage is no-store. Pairing them is common and harmless, but reaching for no-cache when you meant no-store leaves the response sitting in caches you did not intend.

Freshness is counted from when the origin generated the response, not from when your visitor received it. A shared cache reports how far along that clock it is in the Age header, so a response with max-age=600 that spent 500 seconds in a CDN arrives with 100 seconds of life left. s-maxage overrides max-age for shared caches only, which is how you keep a long CDN lifetime while browsers revalidate sooner.

immutable earns its place in one narrow case: it tells the browser not to revalidate even when the user presses reload, which is the one action that otherwise ignores a long max-age. It is only safe on a URL whose content can never change, which in practice means a filename with a content hash in it.

What a header cannot do is reach responses that are already cached. Shortening max-age today does nothing about copies handed out yesterday under the old value, and they stay fresh until their original window expires. That asymmetry is the reason static assets are versioned by URL rather than by header.

Frequently asked questions

When should I use immutable?

Pair it with a long max-age (a year is common) on assets whose filename contains a content hash. The browser then skips the conditional revalidation it would otherwise do on a soft refresh.

no-cache vs no-store?

no-cache means a stored copy must be revalidated before use; no-store means do not store at all. no-store wins when the response contains private data that must never sit on disk.

I set a header and nothing is being cached. What now?

Three things account for most cases. A response that also carries Set-Cookie is treated as personal by shared caches and is usually skipped by a CDN. A request sent with credentials is not stored by a shared cache unless you allow it explicitly. And a browser reload revalidates whatever the header says, so testing with a hard refresh always looks like the header is ignored — open the URL in a new tab instead.

How does this interact with Expires and ETag?

max-age wins over Expires wherever both are present; Expires only matters for caches too old to understand Cache-Control. ETag and Last-Modified are a different mechanism altogether: they do not affect freshness, they make the request cheap once the response has gone stale, letting the server answer 304 with no body. The two are meant to be used together.

Related tools