HTTP Security Headers Explained: A Practical Guide for Developers
August 28, 2026 · 8 min read
Some of the cheapest, highest-impact security wins for any website are not in the application code at all — they are a handful of HTTP response headers. Security headers tell browsers to enforce protections that the server cannot enforce on its own: to refuse downgrades to plain HTTP, to block unauthorized framing of your pages, to reject stylesheets with the wrong MIME type, and to restrict where scripts may load from. This guide walks through the headers that matter, what each one does, and which values to start with.
As you read, you can check your own responses with the free HTTP Header Analyzer, which parses raw response headers, categorizes security and CORS directives, and flags anything missing — entirely in your browser.
Strict-Transport-Security (HSTS)
The first header to add is Strict-Transport-Security. HSTS tells the browser that this domain must only ever be accessed over HTTPS: for the duration of the max-age, any attempt to reach the site over plain HTTP is automatically upgraded before a request even leaves the machine. This closes the window for SSL-strip attacks on public networks.
A sensible production value is Strict-Transport-Security: max-age=63072000; includeSubDomains; preload. The preload directive lets you submit the domain to the browser HSTS preload list, after which the HTTPS-only behavior is baked into browsers themselves. Roll out with a short max-age first (a few minutes), confirm nothing breaks, then raise it.
Content-Security-Policy (CSP)
Content-Security-Policy is the most powerful — and most feared — header in the set. It declares a whitelist of sources from which scripts, styles, images, fonts, frames and connections may load, and the browser blocks anything not on the list. A strict CSP neutralizes most XSS payloads because an injected inline script will not execute unless your policy explicitly allows it.
Start in report-only mode (Content-Security-Policy-Report-Only) to collect violations without breaking the site, then tighten. A modern baseline avoids the old unsafe-inline/unsafe-eval shortcuts by using nonces or hashes for the scripts you control. A reasonable starting policy looks like default-src 'self'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'. Note that frame-ancestors supersedes the older X-Frame-Options.
X-Content-Type-Options
X-Content-Type-Options: nosniff is a one-liner that stops the browser from "MIME sniffing" — guessing what a resource really is. Without it, a file served as text/plain might be interpreted as HTML or JavaScript in some browsers, turning an uploaded text file into an XSS vector. With nosniff, the browser trusts the declared Content-Type and refuses to execute or render resources whose type does not match their context. It also makes browsers block misdelivered stylesheets, which is why a wrong MIME type sometimes breaks all your CSS — check it quickly with the MIME Type Lookup when that happens.
X-Frame-Options and clickjacking
X-Frame-Options controls whether your page may be embedded in an <iframe>. Set it to DENY (or SAMEORIGIN if you legitimately frame yourself) and attackers cannot overlay your site on a malicious page to trick users into clicking — the classic clickjacking attack. For new deployments prefer the CSP frame-ancestors directive, which is more flexible; setting both is harmless for older browsers.
Referrer-Policy and Permissions-Policy
Referrer-Policy governs how much of the current URL leaks to third parties via the Referer header. strict-origin-when-cross-origin (the modern browser default) sends the full URL on same-origin requests but only the origin cross-origin, and nothing on downgrades to HTTP. If your URLs contain tokens or personal data, no-referrer on sensitive pages is safer still.
Permissions-Policy (formerly Feature-Policy) lets you disable powerful browser APIs you do not use — camera, microphone, geolocation, payment: Permissions-Policy: camera=(), microphone=(), geolocation=(). An empty allowlist means the API is disabled for the page and any embedded iframe, shrinking attack surface and preventing third-party widgets from quietly requesting sensors.
Do not forget the caching headers
Security adjacent but equally important: Cache-Control decides whether responses may be stored by browsers and shared caches. Pages containing private data should carry Cache-Control: no-store; static assets with content-hashed filenames can safely use public, max-age=31536000, immutable. Misconfigured caching is responsible for both stale-content bugs and for shared-PC data leaks, so it belongs on every header audit checklist.
Auditing in two minutes
Headers are only effective if they are actually present on production responses — and it is surprisingly common for them to be configured on the origin but stripped by a CDN, or vice versa. Pull the response for your key pages (you can use curl -I https://yoursite or DevTools) and run it through the HTTP Header Analyzer, which marks each security header present or missing, explains CORS and caching directives, and can diff staging against production. Add the headers at your CDN or reverse proxy first — that covers every origin behind it — then verify they survive to the browser.
Security headers are the definition of defense-in-depth: they do not fix vulnerable code, but they make whole classes of attacks fail automatically, for the cost of a few lines of configuration. Add them, report-only first where needed, and re-audit after every infrastructure change.
Try it yourself: HTTP Header Analyzer
Free, instant, and 100% in your browser — no login and no data leaves your device.