Cache-Control Checker

Sends one real HTTP GET request to a URL you choose and classifies the response's caching headers into well-documented misconfiguration patterns: Vary: * (uncacheable by any compliant cache, ever), Cache-Control: public alongside a Set-Cookie header (a shared cache can leak one visitor's cookie to another), plus notices for contradictory or silently-ignored directive combinations and a static asset with no caching headers at all. Only point it at a system you own or are authorised to test.

Chasing a specific symptom? Why isn't my CDN caching this? walks the six response-header reasons a shared cache stores nothing, in the order worth checking them.

1. Run the CLI against your site

Node.js, no dependencies to install:

node check.mjs https://example.com --json

Get check.mjs, probe.mjs, and analyze.mjs from the repo — plain Node, nothing to install. It only ever talks to the URL you pass; nothing goes through this site.

2. Paste the JSON output here

Paste the JSON from step 1 and click Check.

Common questions

Why does Vary: * make a response uncacheable forever?

RFC 9111 defines Vary: * as meaning this response varies on something a cache cannot inspect, so no cache -- browser or CDN -- may ever reuse a stored copy of it, for any later request, no matter how similar. It overrides Cache-Control and Expires entirely: they say a response is cacheable, Vary: * says it can never actually be served from that cache. It usually appears by accident, copied from a template or added defensively, rather than as a deliberate choice.

Why is Cache-Control: public with a Set-Cookie header dangerous?

public tells every shared cache between the server and the visitor -- a CDN edge, a corporate proxy -- that it may store this exact response and serve it to a different visitor. If the response also sets a cookie meant for the one visitor who received it (a session token, a CSRF token, a personalised value), a shared cache that is not specifically configured to strip Set-Cookie on write can hand visitor A's cookie to visitor B on the next request for that URL. This is a real, repeatedly-reported class of CDN misconfiguration, not a hypothetical.

If Cache-Control and Expires disagree, which one wins?

Cache-Control's max-age wins in any HTTP/1.1-or-later-compliant cache; Expires is kept only for the rare cache that predates HTTP/1.1. So if the two headers disagree -- a common drift when one is set by application code and the other by a proxy or CDN default -- only a legacy cache will ever notice, and even then it honours the older, usually-wrong value. Worth syncing the two or dropping Expires entirely rather than leaving them to drift apart.

Why flag a weak ETag on a response that supports byte-range requests?

RFC 9110 requires a strong validator to satisfy a conditional Range request -- a weak ETag (the W/ prefix) cannot be used to confirm a resource hasn't changed since an earlier partial fetch. A client resuming a download, or a media player seeking within a video or audio file, relies on exactly that guarantee. A weak ETag on such a response means range requests either fall back to fetching the whole file again or risk serving inconsistent bytes across a resumed transfer.

Does a clean report mean the site's caching is fully correct?

No. This checks a specific, well-documented set of caching-header patterns from one response -- it is not a full caching or performance audit, does not crawl other pages on the site, and cannot see a misconfiguration this tool's checks don't happen to cover (response security headers like CSP are out of scope here -- see security-headers-check for those). Treat a clean report as evidence about these specific patterns, not a determination that the site's caching strategy is optimal.