← Blog

Security Headers Checklist: The 8 Headers Every Site Should Ship

A site can have perfect application code and still be exploitable through the browser. HTTP security headers are the browser-side lockdown that protects your users from attacks that happen entirely outside your server logic: injected scripts, embedded clickjacking frames, insecure connections, and MIME sniffing. They are free, take minutes to deploy, and are the fastest security upgrade available to any web property.

The catch is that they only help if they are shipped correctly. A misconfigured Content Security Policy can break your own site, and a missing Strict-Transport-Security header leaves your users vulnerable to downgrade attacks. This checklist covers the eight headers that matter, what each one prevents, and the recommended values to ship in production. Check your current deployment against the security headers scanner to see exactly what you are missing.

The complete security headers list

Header Prevents Recommended value
Content-Security-Policy Cross-site scripting, data injection default-src 'self' (then extend per need)
Strict-Transport-Security Protocol downgrade, SSL stripping max-age=31536000; includeSubDomains
X-Content-Type-Options MIME type sniffing nosniff
X-Frame-Options Clickjacking DENY or SAMEORIGIN
Referrer-Policy Referrer leakage strict-origin-when-cross-origin
Permissions-Policy Browser feature abuse camera=(), microphone=(), geolocation=()
Cross-Origin-Opener-Policy Cross-origin window attacks same-origin
Cross-Origin-Resource-Policy Cross-origin resource reads same-origin

Eight headers, one goal: shrink the browser’s attack surface. The table is the checklist; the sections below explain the values and the traps in each one.

The CSP header: most powerful, most dangerous

Content-Security-Policy is the closest thing HTTP has to an allowlist for the browser. It tells the browser which origins may load scripts, styles, images, frames, and other resources. A strict policy stops XSS even when an attacker manages to inject a script tag, because the browser refuses to execute anything that is not on the list.

The value is a set of directives separated by semicolons. A safe, strict starting point is:

default-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'

default-src 'self' means every resource type falls back to the site’s own origin. object-src 'none' disables plugins entirely. frame-ancestors 'none' blocks embedding in frames, which reinforces clickjacking protection. base-uri 'self' prevents base-tag hijacking.

The trap is 'unsafe-inline' and 'unsafe-eval'. Any script you load inline or via eval requires them, and they gut the protection, turning CSP from a lockdown into a formality. If your site needs inline scripts, use a hash or nonce ('nonce-randomvalue') instead. Ship the strict version first, watch the browser console for violations, and relax specific directives only when a legitimate script proves it needs them.

Strict-Transport-Security and the remaining headers

Strict-Transport-Security (HSTS) tells the browser to only ever contact the site over HTTPS for a given period. The recommended value is max-age=31536000; includeSubDomains. The classic deployment mistake is setting a tiny max-age or omitting includeSubDomains, which leaves subdomains reachable over plain HTTP. Only add includeSubDomains once you are certain every subdomain serves HTTPS, or the header will make those subdomains unreachable.

X-Content-Type-Options: nosniff is a single value and always correct. It stops browsers from guessing file types and interpreting an uploaded file as a script. X-Frame-Options: DENY (or SAMEORIGIN if you intentionally embed pages) is the legacy clickjacking guard; CSP frame-ancestors is the modern replacement, and shipping both is fine.

Referrer-Policy: strict-origin-when-cross-origin is the default that leaks the least while remaining practical: full URL to your own site, origin only across origins, nothing on HTTPS-to-HTTP. Permissions-Policy lets you disable browser features like camera and geolocation that your site does not need. Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Resource-Policy: same-origin close the cross-origin window and resource-reading attacks that feed session and data theft.

Shipping the headers in production

Headers belong at the edge, not scattered across application code. Every major platform — Cloudflare, Netlify, Vercel, nginx, and your CDN — has a place to set response headers centrally. A representative nginx block looks like:

add_header Content-Security-Policy "default-src 'self'" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;

The always keyword matters: without it, nginx only adds the header on non-error responses, so your 404 and 500 pages ship without protection. After you deploy, verify the response headers from a fresh curl, then scan the live site to confirm nothing was dropped by middleware or your framework.

Next steps

Run your production site through the security headers scanner, fix every missing header in the checklist, and re-scan after deployment until you reach a clean report.

Try it now: open the security headers tool — free, runs entirely in your browser, nothing is uploaded.