HTTP Security Headers — Complete Guide

Response headers are the cheapest defense-in-depth layer you own: the browser reads them on every navigation and applies policy before your JavaScript runs. Here’s what teams actually ship, how those headers get misconfigured, and copy-paste snippets that fix them on common stacks.

What each header does

HSTS pins HTTPS for a host so bookmarks and typed HTTP URLs upgrade instead of falling through cleartext. You want a sane max-age, includeSubDomains only when every subdomain is TLS-clean, and preload only after you mean it.

CSP lists where scripts, styles, images, frames, and connections may load from. It’s the main XSS mitigation for modern apps when you move off unsafe-inline toward nonces or hashes.

X-Frame-Options (or frame-ancestors in CSP) blocks clickjacking by refusing to render your site inside foreign iframes.

X-Content-Type-Options: nosniff stops the browser from MIME-sniffing responses into executable contexts—especially important for user-upload directories and static hosts that serve text/plain wrong.

Referrer-Policy trims what lands in the Referer header so query tokens and internal paths do not leak to analytics or OAuth intermediaries.

Permissions-Policy disables powerful features (camera, geolocation, payment) until a frame explicitly needs them.

COOP and COEP isolate your browsing context. Pair them when you rely on SharedArrayBuffer or hardened Spectre-related modes; mis-sized CORP on third-party assets is the usual failure mode.

Patterns that break in production

Nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Cross-Origin-Opener-Policy "same-origin" always;

CSP belongs in one line you can iterate—start report-only, then enforce:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:; frame-ancestors 'self'" always;

Apache (mod_headers)

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Content-Type-Options "nosniff" Header always set Referrer-Policy "strict-origin-when-cross-origin"

Vercel (vercel.json)

{ "headers": [ { "source": "/(.*)", "headers": [ { "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" }, { "key": "X-Content-Type-Options", "value": "nosniff" }, { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" } ] } ] }

Edge platforms merge headers from multiple layers—middleware, vercel.json, and upstream. When you see duplicated Content-Security-Policy values in DevTools, check whether both the framework and the edge are emitting policy; browsers intersect or favor the strictest interpretation depending on directive.

Permissions-Policy sample

Start deny-by-default for sensors you do not use, then open specific origins if a payment or maps iframe needs them:

Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self "https://pay.example")

Operational habit

Scan your live deployment, then diff what you ship versus what your HTML actually loads—that is fewer iterations than guessing in staging. Store the header block in version control next to infra code so reviews catch regressions when someone bumps a CDN preset. HeadersFixer reads your site URL and mirrors the stack tabs you already deploy—paste, reload, and verify Security panel before closing the ticket.

Open HeadersFixer →