About the HTTP Headers cheat sheet
HTTP headers are where caching, security, and content negotiation actually happen, and most production problems in those areas are a header that is missing, contradicted, or set at the wrong layer.
The caching headers are the most commonly misconfigured. Cache-Control is what modern caches obey; Expires is a legacy fallback that is ignored when both are present. The distinction between no-cache and no-store matters more than the names suggest — no-cache permits storage but requires revalidation, while no-store forbids storage entirely and is what you want for anything sensitive.
The security headers matter most for what they prevent by default. Content-Security-Policy is the only one that meaningfully limits the damage of an injection, and it is also the one most likely to break a page, which is why report-only mode exists.
Frequently asked questions
What is the difference between no-cache and no-store?
no-cache allows a cache to store the response but requires it to revalidate with the origin before serving it. no-store forbids storing it at all. For pages containing personal data behind a shared cache, no-store is the correct choice — no-cache still permits the response to sit in a cache where a misconfiguration could serve it to someone else.
How do ETag and Last-Modified interact?
Both enable conditional requests. A client sends the ETag back as If-None-Match, or the date as If-Modified-Since, and the server answers 304 if nothing changed. ETag is more precise because it is content-based rather than time-based, and it avoids the one-second granularity problem with dates. When both are present, ETag takes precedence.
Which security headers actually matter?
Content-Security-Policy does the most work by limiting what can execute. Strict-Transport-Security stops the plaintext request happening at all. X-Content-Type-Options set to nosniff prevents content-type confusion attacks. X-Frame-Options is largely superseded by the frame-ancestors directive in CSP but is still worth sending for older clients.
Why is my CORS request failing when the header looks right?
Usually the preflight rather than the actual request. A non-simple request triggers an OPTIONS preflight that must be answered with the allowed methods and headers, and many servers do not route OPTIONS to the handler that sets CORS headers. If credentials are involved, the allowed origin cannot be a wildcard — it must name the origin explicitly.
What does Vary do and why does it matter?
It tells caches which request headers change the response, so a response varying on Accept-Encoding is cached separately per encoding. Getting it wrong causes real incidents: omitting it can serve one user's personalised response to another, while varying on something highly variable like User-Agent fragments the cache so badly that the hit rate collapses.
Need this managed for you, not just automated?
We're also a hands-on DevOps consultancy — Kubernetes, CI/CD, and cloud infrastructure.