FREE TOOL · NO SIGNUP TO PREVIEW

Generate Cache-Control Headers with Toggles

Control exactly how browsers and CDNs cache your pages and assets. Flip toggles for public/private, max-age, immutable and stale-while-revalidate to get a copy-paste Cache-Control header — plus best-practice presets.

Cache directives

Cacheable by

How long the response stays fresh in any cache (browser or shared).

seconds · 1 hour

Overrides max-age for shared caches (CDNs / proxies) only.

seconds

Serve the stale copy this long while revalidating in the background.

seconds

Serve the stale copy this long if the origin returns an error.

seconds
Example output — pick a preset or toggle any directive on the left to build your own Cache-Control header.
publicmax-age=31536000immutable

Cache-Control value

public, max-age=31536000, immutable

The bare value — use it in framework config (e.g. headers()), a CDN rule, or a .htaccess directive.

Full HTTP header

Cache-Control: public, max-age=31536000, immutable

Send this response header from Nginx (add_header), Apache, your origin server, or your CDN.

What this does

Browsers and shared CDNs/proxies may cache this and treats it as fresh for 1 year in any cache. Once fresh, the browser will not revalidate on reload while it is fresh.

Directive breakdown

  • publicAny cache — including shared CDNs and proxies — may store this response.
  • max-age=31536000Fresh for 1 year (31536000s) in any cache before it must be revalidated.
  • immutableWhile fresh, the browser will not revalidate on reload — the body is guaranteed not to change.

Recommended presets by asset type

Hashed static assets

Fingerprinted JS/CSS/fonts (e.g. app.a1b2c3.js). Cache forever — the filename changes on every deploy.

Cache-Control: public, max-age=31536000, immutable
HTML pages

Documents that change. Don’t cache in the browser; let the CDN hold a short copy and revalidate.

Cache-Control: public, max-age=0, s-maxage=60, stale-while-revalidate=86400, must-revalidate
Images & media

Photos, icons, and video. Long-lived on the CDN, shorter in the browser, served stale while refreshing.

Cache-Control: public, max-age=86400, s-maxage=2592000, stale-while-revalidate=604800, no-transform
Authenticated / API

Per-user or sensitive responses. Keep them private to the browser and always revalidate.

Cache-Control: private, no-cache, max-age=0
The Complete Guide

The Complete Guide to the Cache-Control Header

6 MIN READ

Understand with AI

Discuss with your preferred AI assistant

0 ms
Faster repeat loads

A correctly cached asset is served from the browser disk cache instantly, with no network round-trip at all.

1 year
Static asset lifetime

Fingerprinted JS, CSS, and fonts can safely use max-age=31536000, immutable — the URL changes on every deploy.

↓ 90%+
Origin requests saved

A long CDN s-maxage plus stale-while-revalidate can absorb the vast majority of traffic before it reaches your server.

The Cache-Control header is the single most powerful lever you have over web performance. Get it right and repeat visitors load your pages from disk in milliseconds, your CDN absorbs traffic spikes, and your origin server barely breaks a sweat. Get it wrong and you either serve users stale content for days or hammer your server with requests that should never have left the browser.

This guide explains exactly what each Cache-Control directive does, the values you should use for different kinds of assets, and the mistakes that quietly slow sites down. Pair it with the generator above to produce a copy-paste header in seconds.

What Is the Cache-Control Header?

Cache-Control is an HTTP response header that tells browsers and shared caches (CDNs and proxies) whether they may store a response, how long it stays fresh, and what to do once it goes stale. It is defined by RFC 7234 and is supported by every modern browser and CDN.

A typical value looks like public, max-age=31536000, immutable. Each comma-separated token is a directive, and together they form the caching policy for that response.

The Core Directives, Explained

Most real-world headers are built from a small set of directives:

  • public / private — public lets any cache, including shared CDNs, store the response. private restricts it to the end user's browser, which is essential for personalized or authenticated content.
  • max-age=N — the response is fresh for N seconds in any cache before it must be revalidated. This is the directive you will use most.
  • s-maxage=N — overrides max-age for shared caches only. It lets you cache aggressively on the CDN while keeping a shorter lifetime in the browser.
  • no-cache — the response may be stored, but a cache must revalidate it with the origin before every reuse. Despite the name, it does not mean "don't cache."
  • no-store — nothing is ever written to any cache. This is the only directive that truly disables caching, and it overrides everything else.
  • must-revalidate — once a response is stale, a cache must check with the origin and must never serve the stale copy.
  • immutable — promises the body will not change while it is fresh, so the browser skips revalidation even on reload. Ideal for fingerprinted files.
  • stale-while-revalidate=N — for N seconds after going stale, a cache may serve the old copy instantly while fetching a fresh one in the background.

There is no single "best" header — the right policy depends on whether the file's contents can change at its URL. Here are battle-tested defaults.

Asset typeRecommended Cache-ControlWhy
Fingerprinted JS/CSS (app.a1b2c3.js)public, max-age=31536000, immutableThe filename changes on every deploy, so the file at a given URL never changes. Cache it for a year.
HTML pagespublic, max-age=0, s-maxage=60, must-revalidate, stale-while-revalidate=86400Content changes, so the browser always revalidates, but the CDN holds a short copy and serves stale during refresh.
Images & mediapublic, max-age=86400, s-maxage=2592000, stale-while-revalidate=604800, no-transformLong-lived on the CDN, refreshed daily in the browser, and protected from proxy recompression.
Authenticated / API responsesprivate, no-cachePer-user data must never land in a shared cache and should always be revalidated for freshness.

How to Set the Cache-Control Header

1. Decide if the content at the URL can change

This is the key question. If the URL is fingerprinted (the filename includes a content hash), the file is effectively immutable and you can cache it forever. If the same URL can return different content over time — like an HTML page — you need short or zero browser caching with revalidation.

2. Split the browser and CDN lifetimes

Use max-age for the browser and s-maxage for the CDN. A common pattern for dynamic HTML is a near-zero max-age with a longer s-maxage, so users always get a freshly revalidated page while your origin is shielded by the edge.

3. Add stale-while-revalidate for a smoother experience

Adding stale-while-revalidate lets caches serve the previous version instantly while quietly fetching a new one. Visitors never wait on a revalidation round-trip, and the cache stays warm.

4. Emit the header from the right place

You can set the header in your framework config, your origin server (Nginx add_header, Apache, or a .htaccess rule), or directly on your CDN. Setting it once at the edge keeps the policy consistent across every response.

Common Cache-Control Mistakes

  • Caching HTML forever. If an HTML page has a long max-age, users keep seeing the old version until the cache expires — there is no way to push an update.
  • Using no-cache to disable caching. no-cache still stores the response; only no-store prevents caching entirely.
  • immutable without a max-age. immutable only applies while a response is fresh, so it does nothing without a positive max-age.
  • private with s-maxage. s-maxage targets shared caches, which private already forbids — the directive is silently ignored.
  • Forgetting fingerprints. Long-caching an unhashed asset means you cannot ship a fix without renaming the file or busting the cache manually.

Expert Tips

Fingerprint, then cache forever

Add a content hash to static asset filenames so each URL is immutable, then cache them for a year with the immutable directive. New deploys ship new URLs, so users never see stale code.

Keep HTML fresh, shield the origin

Give HTML a near-zero browser max-age with must-revalidate, but a short s-maxage and a stale-while-revalidate window on the CDN. Users always get current content while your origin handles only a trickle of traffic.

Frequently Asked Questions

What is the difference between max-age and s-maxage?

max-age sets the freshness lifetime for every cache, including the browser. s-maxage applies only to shared caches like CDNs and proxies, and when present it overrides max-age there. Use s-maxage to cache aggressively at the edge while keeping a shorter lifetime in the browser.

Does no-cache mean the response is not cached?

No. no-cache allows a cache to store the response but requires it to revalidate with the origin before every reuse. The only directive that prevents storage entirely is no-store, which you should reserve for sensitive or always-fresh data.

When should I use the immutable directive?

Use immutable for fingerprinted static assets whose URL changes on every deploy, paired with a long max-age such as one year. It tells the browser the body will never change while fresh, so it skips revalidation even when the user reloads the page — eliminating needless conditional requests.

What Cache-Control header should HTML pages use?

HTML usually changes, so avoid long browser caching. A robust pattern is a near-zero max-age with must-revalidate plus a short s-maxage on the CDN and a stale-while-revalidate window. Visitors always get a revalidated page, your origin is protected, and updates appear immediately.

Related guides

Related tools