How to Diagnose Missing 304 Responses
When every request returns 200 despite validators, the conditional request handshake is often broken.
Symptom definition
- Repeated fetches of the same URL keep returning 200 instead of 304
- ETag or Last-Modified is present in responses
- Even low-change resources are repeatedly downloaded
Diagnostic steps
- 1) Use Cache Diagnostic to confirm validator presence in responses
- 2) Use If-None-Match Inspect to ensure re-requests include If-None-Match
- 3) Use If-Modified-Since Inspect to validate date-based revalidation flow
- 4) Use ETag Inspect to check weak/strong ETag handling differences
- 5) If CDN is involved, use HTTP Cache Mismatch to isolate layer differences
Common causes
- Conditional If-* headers are not sent on re-requests
- ETag changes per path due to compression variance or server differences
- Cache-Control uses no-store, preventing storage before revalidation
- Time skew causes unstable If-Modified-Since evaluation
Fix checklist
- Emit stable ETag and/or Last-Modified (preferably both)
- Use no-store and no-cache intentionally for different goals
- Verify expected 200→304 behavior before release
- Keep validator values aligned between CDN and origin
Tools to use
- Cache Diagnostic
- ETag Inspect
- If-None-Match Inspect
- If-Modified-Since Inspect
- Last-Modified Inspect
- Cache Response Analyzer
FAQ
- Why does 304 not happen even when ETag is present?
- Most commonly, If-None-Match is missing on re-requests or ETag changes across delivery layers.
- Can 304 work with Last-Modified only?
- Yes. It works if If-Modified-Since round trips correctly. Using ETag together is usually more stable.
Referenced specs
Next to view (diagnostic order)
These links are generated from site_map rules in recommended diagnostic order.
- Cache Diagnostic — Run cross-header diagnostics for HTTP caching
- ETag Inspect — Parse ETag and If-None-Match consistency
- If-None-Match Inspect — Parse If-None-Match and inspect revalidation conditions
- If-Modified-Since Inspect — Parse If-Modified-Since and inspect conditional retrieval
- Last-Modified Inspect — Parse Last-Modified and If-Modified-Since
- Cache Response Analyzer — Judge cacheability from response headers
- Symptom-Based Diagnostic Guide (Start Here) — A central hub that routes cache/CORS/JWT/MIME incidents into shortest symptom-first diagnostic paths
- How to Diagnose Stale Content After Deployment — Check cache policy by HTML/API/static assets to isolate stale deployment issues quickly
Same-theme links
Scenario Clusters
Operational incident scenarios that route you into the shortest diagnostic path
- Symptom-Based Diagnostic Guide (Start Here) — A central hub that routes cache/CORS/JWT/MIME incidents into shortest symptom-first diagnostic paths
- How to Diagnose Stale Content After Deployment — Check cache policy by HTML/API/static assets to isolate stale deployment issues quickly
- How to Diagnose CORS Preflight Failures — Fix preflight failures by validating OPTIONS responses, Allow-* directives, and origin rules in order
- JWT 401/403 Diagnostic Playbook — Separate 401 and 403 using Authorization, WWW-Authenticate, claims, and signature checks
- How to Diagnose Retry Storms on 429/503 — Isolate Retry-After parsing and client implementation gaps to stop excessive retries
- How to Diagnose JS/CSS Blocks from nosniff Mismatch — Trace Content-Type vs nosniff mismatches, fallback responses, and delivery-layer rewrites
- How to Diagnose Set-Cookie Not Persisting — Isolate cookie persistence failures by checking Domain/Path/Secure/SameSite in order
- How to Diagnose Lost Login After OAuth Return — Isolate cookie-delivery failures after IdP return across SameSite, Secure, Path/Domain, and collisions
- How to Diagnose Same-Name Cookie Collisions — Resolve unstable behavior by tracing same-name cookie path/domain variants, overwrite order, and send collisions
- Cookie Incident Operational Checklist — Standardize response from triage to permanent fixes across storage failures, OAuth return issues, and same-name collisions