How to Debug Website Caching Issues Using HTTP Headers
Stale content complaints and mysterious re-downloads almost always trace back to a handful of caching headers. Here's how to read them correctly.
📅 Published July 2026·⏳ 9 min read·✍️ ToolsNovaHub Editorial Team
Caching bugs are notoriously frustrating because the symptoms — stale content, inconsistent behavior between users, unexpected bandwidth usage — rarely point directly at the cause. The good news: the actual answer is almost always visible in a small set of response headers, once you know what to look for.
Cache-Control is the primary header governing caching behavior, and most caching bugs trace back to a misconfigured or missing value here. Key directives:
Directive
Meaning
no-store
Never cache this response anywhere
no-cache
Can be cached, but must revalidate with the server before using the cached copy
max-age=N
Cache is considered fresh for N seconds before revalidation is needed
public
Can be cached by shared caches (CDNs, proxies), not just the browser
private
Only the end user's browser may cache it — not shared caches or CDNs
A common bug: setting private on content meant to be CDN-cached, causing the CDN to bypass its cache entirely and hit your origin server on every request.
ETag & Validation-Based Caching
ETag is a unique fingerprint for a specific version of a resource. On a follow-up request, the browser sends the stored ETag back via If-None-Match; if it still matches, the server responds with a lightweight 304 Not Modified instead of re-sending the full content. If you're seeing full re-downloads when you expected a 304, check whether the ETag value is actually stable across requests — some misconfigurations (like including a server instance ID or timestamp in the ETag) cause it to change on every request even when content hasn't, defeating validation entirely.
Last-Modified
A simpler, less precise alternative to ETag — a timestamp of when the resource was last changed. The browser sends it back via If-Modified-Since on follow-up requests. Less precise than ETag (only second-level granularity, and can't distinguish content changes that don't affect the modification timestamp), but simpler to implement correctly and still widely used, often alongside ETag as a fallback.
The Vary Header Trap
Vary tells caches which request headers should be factored into cache key uniqueness — commonly Vary: Accept-Encoding to cache compressed and uncompressed versions separately. A frequent, hard-to-spot bug: setting Vary: User-Agent or similar high-cardinality headers, which can fragment cache effectiveness dramatically since nearly every visitor has a slightly different User-Agent string, causing far more cache misses than intended.
CDN-Specific Cache Headers
Most CDNs add their own diagnostic headers indicating cache status directly — Cloudflare's CF-Cache-Status (HIT/MISS/EXPIRED/BYPASS), Fastly's X-Cache, and similar equivalents elsewhere. These are often the fastest way to confirm whether a request actually hit cache or went to origin, without needing to reason through Cache-Control logic manually.
A Practical Debugging Workflow
1
Check Cache-Control First
Confirm the directive present matches your intent — public vs private, and the max-age value, are the most common misconfiguration points.
2
Check CDN Cache Status Header
If using a CDN, its cache-status header tells you immediately whether the request hit cache or went to origin.
3
Verify ETag Stability
Request the same resource twice and confirm the ETag value is identical if content hasn't changed — instability here defeats validation caching entirely.
4
Check for an Overly Broad Vary Header
A high-cardinality Vary value can silently fragment your cache into thousands of near-unique entries.
Use our HTTP Headers Checker to quickly pull the full caching-related header set for any URL without needing to open browser DevTools.
FAQs
What is the most important caching header to check first? +
Cache-Control — it's the primary directive governing caching behavior and the most common source of misconfiguration.
What is the difference between no-cache and no-store? +
no-cache allows caching but requires revalidation with the server before use; no-store forbids caching entirely, anywhere.
Why would I see a full re-download instead of a 304 Not Modified? +
Often because the ETag value is unstable — changing on every request even when content hasn't, which defeats validation-based caching entirely.
What does the Vary header actually do? +
Tells caches which request headers should factor into cache key uniqueness — commonly Accept-Encoding to cache compressed and uncompressed content separately.
Why is Vary: User-Agent considered risky? +
User-Agent strings have extremely high cardinality (nearly every visitor differs slightly), so varying cache keys on it can fragment cache effectiveness dramatically, causing far more misses than intended.
What is CF-Cache-Status? +
Cloudflare's diagnostic header directly indicating whether a specific request was served from cache (HIT), fetched from origin (MISS), or bypassed caching entirely.
What's the difference between ETag and Last-Modified? +
ETag is a precise content fingerprint; Last-Modified is a simpler timestamp with only second-level granularity — many systems use both together for robustness.
Why would setting Cache-Control: private cause CDN caching problems? +
private restricts caching to the end user's own browser only — a CDN or shared proxy in front of your origin will bypass caching entirely and hit origin on every request.
How do I check response headers without opening browser DevTools? +
Use a dedicated tool like our HTTP Headers Checker, which performs an independent check and groups caching-related headers for easy review from any device.
What does max-age=0 mean? +
The cached content is considered immediately stale, forcing revalidation with the server on every use even if the response is technically cacheable.
Can stale content still appear even with correct Cache-Control settings? +
Yes — intermediate caches (browser extensions, corporate proxies, ISP-level caching) don't always strictly respect all directives, which is a common source of confusing caching behavior outside your direct control.
Why might two users see different cached versions of the same page? +
Differences in Vary-based cache keys, A/B testing cookies factored into cache logic, or simply different CDN edge locations with independent cache states can all cause this.
Is it bad practice to disable caching entirely with no-store? +
Not inherently — it's the correct choice for genuinely dynamic, sensitive, or frequently-changing content, but overusing it unnecessarily increases server load and slows down page loads for content that could safely be cached.
How often should static assets use long max-age values? +
For assets with content-hashed filenames (like style.a3f21b.css), very long max-age values (a year or more) are standard practice since any content change produces a new filename entirely.
Does compression affect caching headers? +
Yes — Content-Encoding indicates what compression was applied, and Vary: Accept-Encoding is typically needed to correctly cache compressed and uncompressed variants as separate cache entries.