HTTP cache tester: will this response be cached?

Paste the headers you are about to ship, or the ones you just pulled out of a log, and pick the cache you care about. You get four answers — stored, how long it stays fresh, whether the next request avoids the origin, and which line decided each one.

Runs entirely in your browser. Your data is never uploaded.

A status line is optional — without one the response is read as a 200. HTTP/2 pseudo-headers (:status) work, so a paste straight out of your browser's network panel is fine.

Only the method and two headers change the answer — Authorization and Cache-Control — but the rest does no harm.

Which cache?
Examples

    Whose answer this is

    Caching questions are usually answered by quoting RFC 9111 at each other, and the quoting is where the mistakes come from. This page instead reproduces one real implementation: http-cache-semantics 4.2.0, the cache-decision engine behind got, cacheable-request and make-fetch-happen. Every verdict above is recorded from that library running for real and replayed as a test, so what you see is what it decides — not what we think the specification says.

    That library is not the specification, and in a handful of places the two differ. Those places are named on the page, under the verdicts, whenever your headers reach one: what this engine does, and what RFC 9111 says. Your CDN is a third implementation again, so treat this as a precise answer from one well-used engine rather than a promise about a particular vendor.

    The four questions, in the order a cache asks them

    1. May I store this at all? The request method, the status code, no-store, private and an Authorization request header all decide this before any lifetime is worked out.
    2. How long does it stay fresh? s-maxage beats max-age beats Expires — but only for a shared cache. With none of them, a cache may guess from Last-Modified.
    3. How old is it already? The Age header counts time the response spent in caches upstream, and it is subtracted from the lifetime before anything else happens.
    4. Can I answer this request from the copy? Freshness is necessary and not sufficient: no-cache, must-revalidate, Vary and the request's own directives all get a say.

    What each directive does, in each kind of cache

    Every cell is produced by running this page's engine, and checked by a test — so this table cannot drift from the behaviour above. The responses carry a Date header and nothing else.

    Cache-Control Shared cache (CDN) Private cache (browser) What it means
    max-age=600 Stored, fresh for 10 minutes Stored, fresh for 10 minutes The plain answer: every cache may keep it, and it is fresh for ten minutes from the response Date.
    s-maxage=600 Stored, fresh for 10 minutes Stored, stale immediately Only a shared cache sees this. A browser has no lifetime from it at all, so it revalidates on every use.
    max-age=60, s-maxage=600 Stored, fresh for 10 minutes Stored, fresh for 1 minute The common CDN pattern: a long life at the edge, a short one in the browser. s-maxage also overrides Expires.
    no-cache Stored, stale immediately Stored, stale immediately Store it, but never serve it without asking the origin first. This is not "do not cache" — the copy is kept, and a 304 makes reuse cheap.
    no-store Not stored Not stored The one that really means do not keep it. Nothing is written down, so there is nothing to revalidate.
    private, max-age=600 Not stored Stored, fresh for 10 minutes The browser keeps it; the CDN refuses it outright. This is how a per-user page stays out of a shared cache.
    public, max-age=600 Stored, fresh for 10 minutes Stored, fresh for 10 minutes public mainly matters when something else would have blocked storage — an Authorization request header, or a Set-Cookie in the response.
    must-revalidate, max-age=600 Stored, fresh for 10 minutes Stored, fresh for 10 minutes Forbids serving the response once it is stale. This engine goes further and revalidates every time — see the divergences below.
    proxy-revalidate, max-age=600 Stored, stale immediately Stored, fresh for 10 minutes The same idea aimed at shared caches only; a browser ignores it.
    public, max-age=31536000, immutable Stored, fresh for 1 year Stored, fresh for 1 year The fingerprinted-asset pattern. immutable stops a browser revalidating on a reload while the response is still fresh.
    max-age=60, stale-while-revalidate=600 Stored, fresh for 1 minute Stored, fresh for 1 minute For ten minutes after it goes stale, the stored copy is served straight away and refreshed in the background.
    max-age=60, stale-if-error=600 Stored, fresh for 1 minute Stored, fresh for 1 minute Keeps the copy usable for ten minutes past staleness, but only if the origin fails. It shows up as a longer time-to-live, not a longer freshness lifetime.

    Results that surprise people

    Each verdict here is the one this tool gives, computed when the page was built. Every response also carries Date: Thu, 01 Jan 2026 00:00:00 GMT, which is left out of the column below only to keep it readable — the lifetimes are measured from it. Paste any case above to see the deciding line.

    Case Result Why it catches people
    A stray Set-Cookie Cache-Control: max-age=600 Set-Cookie: session=abc; Path=/; HttpOnly Shared cache Stored — stale immediately The response is stored, and it is stale the instant it arrives, so every request still reaches the origin. This looks exactly like a cache that is working and never hits.
    s-maxage, seen from a browser Cache-Control: max-age=60, s-maxage=600 Private cache Stored — fresh for 1 minute The same header that gives the CDN ten minutes gives the browser one. Test the cache you are actually asking about.
    no-cache Cache-Control: no-cache Shared cache Stored — stale immediately no-cache stores the response. It is no-store that does not. Reaching for no-cache to keep something out of a cache stores it anyway.
    An Authorization header Authorization: Bearer eyJhbGci… Cache-Control: max-age=600 Shared cache Not stored A shared cache will not store a response to an authenticated request at all unless the response says public, must-revalidate or s-maxage. The browser stores it regardless.
    No caching headers at all Last-Modified: Mon, 22 Dec 2025 00:00:00 GMT Shared cache Stored — fresh for 1 day Saying nothing does not mean "do not cache". With a Last-Modified ten days old, a cache may invent a lifetime of a tenth of that age — a whole day.
    Vary: * Cache-Control: max-age=600 Vary: * Shared cache Stored — stale immediately A stored copy is selected by matching the headers named in Vary. * never matches anything, so the copy is kept and never used.
    Expires: 0 Expires: 0 Shared cache Stored — stale immediately The decades-old way of writing "already expired" does work — but not for the reason it is usually given. This engine hands the value to JavaScript's date parser, which reads 0 as the year 2000 rather than rejecting it, and a date that far in the past is expired either way. A value it genuinely cannot read, such as Expires: never, is also treated as expired.
    A permanent redirect with no headers HTTP/1.1 301 Shared cache Stored — stale immediately A 301 is storable by default, and with nothing to give it a lifetime it is stale at once. Browsers are far stickier about redirects than this; give one an explicit max-age rather than relying on either behaviour.
    A miscased directive Cache-Control: Max-Age=600 Shared cache Stored — stale immediately This engine matches directive names exactly, so it reads nothing here. RFC 9111 makes them case-insensitive and a real cache would honour it — the tool flags the line rather than letting the verdict stand alone.

    Three things this page cannot tell you

    About this tool

    The decisions are a TypeScript port of http-cache-semantics 4.2.0, pinned by 758 recorded cases — curated ones for every directive, status and header that changes an answer, plus a seeded sweep through combinations nobody thought to curate — generated by running the library itself and replayed against the port on every build. The header parser is ours, and it reports what it made of your paste rather than guessing quietly.

    Everything runs in your browser. Headers routinely carry internal hostnames, session cookies and bearer tokens, so nothing you paste is uploaded, logged or sent anywhere — which is also why this tool takes pasted headers instead of fetching a URL for you.

    Includes a derivative work of http-cache-semantics, Copyright 2016–2018 Kornel Lesiński, licensed under the BSD 2-Clause licence. The upstream licence is kept in third-party/http-cache-semantics/ in this project's repository.