Cache-Control Header Builder

Builds the line a web server sends to tell browsers how long they may keep their copy of a page. It also keeps two look-alikes straight: no-cache stores the response and checks it is still current, while no-store keeps nothing at all.

How to use it

Directives are ticked one at a time and the header assembles as you go, with a sentence under each saying what it actually does.

  1. Pick a preset Hashed asset, HTML page, API response or private — these cover almost every case.
  2. Adjust the times All in seconds. A year is 31536000.
  3. Read what it does Every directive is spelled out, so nothing depends on remembering what s-maxage means.
Full instructions

What this does

    In depth

    Building a Cache-Control header that does what you think it does

    What each cache directive actually tells browsers and CDNs, why no-cache and no-store are almost opposites despite sounding alike, how the four presets cover most real websites, and a full walkthrough of a header for a hashed JavaScript bundle.

    Screenshot: The two directives everyone mixes up
    The two directives everyone mixes up as it appears when the page opens.

    The header and why it matters

    Cache-Control is an HTTP response header. It tells every cache between the server and the user - the browser, a CDN, a corporate proxy - how long they may keep a copy and under what conditions they must check back.

    Getting it right makes a site fast. A browser that already has a cached copy of a stylesheet does not need to download it again. Getting it wrong makes a site stale. A browser that caches an HTML page for a year will show outdated content to every visitor until that year runs out, and there is no way to take the instruction back once it has been sent.

    The header is a comma-separated list of directives. Each directive is a word, sometimes followed by an equals sign and a number. The order does not matter. Some directives work alone. Some only make sense in combination. A few combinations are actively misleading, and the tool warns you when you build one.

    The four presets

    The panel on the right starts with four buttons: Hashed asset, HTML page, API response and Private. Each one fills in the controls below with a sensible starting point.

    Hashed asset sets a max-age of one year with public, immutable. This is the standard recipe for a CSS or JavaScript file whose URL contains a content hash - something like app.3f8a2c.js. Because the hash changes whenever the content changes, the old URL will never be requested again, and a one-year cache is perfectly safe.

    HTML page sets a short max-age or uses no-cache, because the HTML file is the one whose URL does not change. It needs to be checked on every visit so the browser picks up the new stylesheet and script URLs.

    API response is similar to the HTML preset but may add stale-while-revalidate so the user gets a fast answer while a fresh one is fetched behind the scenes.

    Private sets the visibility to private, meaning only the end user's browser may cache the response. A CDN or shared proxy must not keep a copy. This is the right choice for any page that shows data belonging to a logged-in user.

    The directives one by one

    The Caching dropdown has three modes. Cache for a time is the normal case: the response is stored and reused for the number of seconds you set in max-age. Store but always revalidate adds the no-cache directive, which stores a copy but requires the cache to check with the server before using it. Never store adds no-store, which tells caches not to keep a copy at all.

    Who may cache it controls visibility. Anyone, including CDNs adds public. Only the user's browser adds private. Do not say leaves it unspecified, which usually behaves like private but is less explicit.

    The max-age field is in seconds. Common values are 0 (do not use without checking), 3600 (one hour), 86400 (one day) and 31536000 (one year). The s-maxage field overrides max-age for shared caches only. Use both when you want a CDN to hold content for an hour while browsers hold it for five minutes.

    The stale-while-revalidate field lets caches serve an expired copy while fetching a fresh one in the background. It is almost free speed. The user sees the old response instantly rather than waiting for the network round trip, and the next user gets the fresh one.

    The stale-if-error field lets caches serve an expired copy when the server is down. It is a safety net that keeps the site looking alive during a short outage.

    The three checkboxes at the bottom add immutable, must-revalidate and no-transform. Immutable tells the browser not to bother checking even on a hard refresh, because the content at this URL will never change. Must-revalidate forbids stale responses. No-transform tells proxies not to compress or convert images on the fly.

    The output panel

    The Header text area shows the complete header value, ready to copy. The Copy button puts it on the clipboard.

    Below the header is a section called What this does. It lists each directive in plain words. Instead of requiring you to remember that s-maxage overrides max-age for shared caches, it says so in a sentence. This is the part to read when you are not sure what you just built.

    If the combination you have chosen is contradictory or misleading, a warning appears above the output. For example, setting public together with no-store is technically allowed by the specification but almost certainly not what you meant. The warning explains why and suggests a fix.

    A walkthrough: caching a hashed JS bundle

    You have a JavaScript file called main.a7b3f9.js. The hash in the filename changes every time you rebuild, so no two versions share a URL. You want browsers and CDNs to cache it for as long as possible.

    Click Hashed asset. The controls fill in: Caching is set to Cache for a time, Who may cache it is Anyone, including CDNs, max-age is 31536000, immutable is ticked. The header reads public, max-age=31536000, immutable.

    The What this does section says the response may be stored by any cache, it is valid for one year, and the browser will not revalidate even on a hard refresh. That is exactly right for a hashed file.

    Now suppose you want the CDN to recheck every hour while browsers keep the year-long cache. Type 3600 into the s-maxage field. The header now reads public, max-age=31536000, s-maxage=3600, immutable. The explanation updates to say that shared caches override the year-long lifetime with a one-hour one. Browsers still hold their copy for a year. The CDN refreshes every hour.

    The confusion between no-cache and no-store

    This is the single most misunderstood pair of directives in all of HTTP. The names suggest they are close relatives. They are nearly opposites.

    no-cache stores a copy and checks with the server before every use. If the server replies with a 304 Not Modified, the cached copy is used and no download happens. This is efficient. It means the browser always has the latest version but avoids a full download when nothing has changed.

    no-store instructs caches not to keep a copy. Every request goes to the server and downloads the full response. This is the right choice for a page that shows a bank balance or a medical record, where even a brief cached copy is a problem.

    The confusion costs real money. A developer who writes no-cache when they mean no-store leaves sensitive data in the browser cache. A developer who writes no-store when they mean no-cache forces a full download on every page load, which slows the site down and increases bandwidth costs.

    Mistakes that bite later

    • Setting a long max-age on a URL that does not change when its content does. A browser that cached the old version will not check again for that entire duration. Content-hashed filenames solve this for assets. For HTML pages, use no-cache or a very short max-age.
    • Using public for a page that shows user-specific data. A CDN or shared proxy might serve one user's page to another. Use private for anything behind a login.
    • Forgetting stale-while-revalidate. It is free speed with almost no downside. The user gets the cached copy instantly and the fresh one arrives in the background for the next visitor.
    • Setting immutable on a URL that does change. The browser will not check even on a hard refresh, so the only way to get the new version is to clear the cache manually.
    • Assuming the CDN obeys the header. Many CDN products have their own cache rules that override origin headers. Always check the CDN dashboard if caching is not working as expected.

    When another tool is a better fit

    • For inspecting the headers an actual server is sending, the browser's network panel in developer tools shows every header on every response.
    • For testing whether a cached resource is being revalidated, the same network panel marks 304 responses with a different colour.
    • For building other HTTP headers like Content-Security-Policy or CORS, dedicated generators exist for each.
    • For measuring how much caching helps a particular page, a performance audit tool like Lighthouse gives a concrete score.

    Nothing is sent anywhere

    The header is built entirely in your browser. The directives you choose and the values you type are processed locally. There is no server, no account and no saved history.

    This matters when you are setting up caching for an internal application whose URL structure you would rather not share. Build the header here, copy it, and paste it into your configuration. The tool never sees the URLs the header will apply to.

    Help

    The two directives everyone mixes up

    Directives are ticked one at a time and the header assembles as you go, with a sentence under each saying what it actually does. Combinations are where these headers go wrong, so contradictory ones are flagged rather than quietly built: the pairs that read as though they mean one thing and instruct another.

    Pick a preset

    Hashed asset, HTML page, API response or private — these cover almost every case.

    Adjust the times

    All in seconds. A year is 31536000.

    Read what it does

    Every directive is spelled out, so nothing depends on remembering what s-maxage means.

    Hash your filenames

    Immutable year-long caching plus a content hash in the filename is the whole modern asset strategy. Without the hash, the long cache is a liability.

    Good to know

    no-cache does cache

    It stores the response and revalidates before reuse. If you meant "never keep a copy", you want no-store.

    A long max-age cannot be withdrawn

    A browser that cached it will not ask again for that long. Only safe on a URL that changes when the content does.

    stale-while-revalidate is free speed

    The user gets the cached copy instantly while a fresh one is fetched behind them. Almost always worth setting.

    Common questions

    max-age or s-maxage?
    max-age applies to everything; s-maxage overrides it for shared caches only. Use both to let a CDN hold content longer than a browser.
    Does no-store guarantee nothing is written to disk?
    It instructs caches not to store. It is not a security guarantee — do not rely on it to protect secrets.
    What about ETag and Last-Modified?
    They make revalidation cheap by allowing a 304. They complement Cache-Control rather than replacing it.
    Why is my CDN ignoring this?
    Many CDNs have their own override settings that win over origin headers. Check the CDN configuration first.