Every security header guide gives you the same list, formatted as a checklist where every row looks equally important. They are not equally important. Some prevent entire attack classes; others were deprecated years ago and survive in copy-pasted nginx configs.
Here they are ranked by how much they actually buy you, with the obsolete ones at the bottom so you can delete them.
1. Content-Security-Policy
By a wide margin the most valuable header, and the only one that meaningfully mitigates cross-site scripting. It tells the browser which sources of script, style, and other content are permitted, so an injected <script> simply does not execute.
It is also the hardest to deploy, which is why so many sites either skip it or ship something toothless. A policy containing unsafe-inline for scripts provides essentially no XSS protection — and that is the most commonly deployed policy in the wild, because removing inline scripts from a real application is genuinely hard work.
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{RANDOM}' 'strict-dynamic';
object-src 'none';
base-uri 'none';
report-uri /csp-report
A nonce-based policy with strict-dynamic is the modern approach and it is more practical than host allowlists, which are notoriously easy to bypass via a permitted CDN that happens to host a vulnerable library. Deploy it in Content-Security-Policy-Report-Only mode first and collect violations for a couple of weeks — you will discover inline handlers nobody remembered. Our CSP Level 3 and Trusted Types guide covers the rollout in detail.
Worth the effort: yes, more than everything below it combined.
2. Strict-Transport-Security
HSTS tells the browser to use HTTPS for this domain, always, for the specified duration — even if a user types http:// or clicks an old link. It closes the window where an initial plaintext request can be intercepted and downgraded.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The one caution is that it is a commitment. Once a browser has seen this header, it will refuse plaintext for a year, and includeSubDomains extends that to every subdomain — including internal ones that may not have certificates. Start with a short max-age, verify nothing breaks, then raise it. The preload directive ships your domain in browsers’ built-in lists, which protects even a first-ever visit, but removal from those lists is slow.
Worth the effort: yes. Nearly free once your TLS is in order.
3. X-Content-Type-Options
One value, no configuration, and it prevents a real attack:
X-Content-Type-Options: nosniff
Without it, browsers may ignore your declared Content-Type and guess from content. A user-uploaded file served as text/plain but containing HTML and script can be sniffed as HTML and executed. Anywhere you serve user-uploaded content, this matters.
Worth the effort: yes. It is one line with no downside.
4. Set-Cookie attributes
Not a header of its own, but the cookie attributes do more for real-world security than most of the dedicated headers below.
Set-Cookie: session=…; HttpOnly; Secure; SameSite=Lax; Path=/
HttpOnly puts the cookie out of reach of JavaScript, so an XSS bug cannot exfiltrate the session token — genuinely valuable defence in depth given how hard XSS is to eliminate. Secure prevents transmission over plaintext. SameSite=Lax stops the cookie riding along on most cross-site requests, which removes the majority of CSRF without a token.
Use SameSite=Strict for anything genuinely sensitive, accepting that a user following a link from another site will arrive logged out. Avoid SameSite=None unless you truly need cross-site cookies, and note it requires Secure.
Worth the effort: yes, and it is frequently overlooked in favour of flashier headers.
5. X-Frame-Options / frame-ancestors
Prevents your page being embedded in an iframe on someone else’s site, which is the mechanism behind clickjacking — an invisible overlay tricking users into clicking something they cannot see.
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
frame-ancestors is the modern replacement and is strictly more capable, since it accepts a list of permitted origins. Send both while older browsers persist in your traffic; where the two conflict, frame-ancestors wins in browsers that support it.
Worth the effort: yes, though the attack is narrower than the previous four.
6. Referrer-Policy
Controls how much URL information travels in the Referer header when a user leaves your site. The concern is leaking sensitive data through URLs — password reset tokens, internal identifiers, search queries — to whatever third party the user clicks through to.
Referrer-Policy: strict-origin-when-cross-origin
That value sends the full path within your own site, but only the origin cross-site, and nothing at all when downgrading to HTTP. It is a sensible default and now matches what most browsers do anyway, which is why this ranks mid-table rather than higher.
Worth the effort: yes, but the browser default is already reasonable.
7. Permissions-Policy
Controls access to browser features — camera, microphone, geolocation, and others — for your page and, importantly, for any iframes it embeds.
Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=()
The real value is constraining third-party embeds. If you embed a widget, this stops it requesting the camera regardless of what its code attempts. For a site with no embeds and no feature usage, it is close to a no-op — users would still get a permission prompt they could decline. Useful, but not urgent.
Worth the effort: moderate. Prioritise it if you embed third-party content.
8. Cross-Origin-Opener-Policy and friends
COOP, COEP, and CORP isolate your page from cross-origin windows and resources. They are the prerequisite for cross-origin isolation, which browsers require before granting access to SharedArrayBuffer and high-resolution timers.
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Be aware these are disruptive. require-corp will break every cross-origin resource that does not explicitly opt in, which in practice means auditing every image, script, and font you load from elsewhere. Worth doing if you need the isolated features or handle genuinely sensitive data in-page; otherwise the disruption outweighs the benefit for most sites.
Worth the effort: situational.
9. The ones you can delete
Three headers still appear in configuration templates and guides, and all three should be removed.
X-XSS-Protection — controlled a browser XSS filter that no longer exists in any current browser. Worse, the filter itself introduced vulnerabilities, which is why it was removed. Setting it to 0 is harmless; setting it to 1; mode=block is cargo cult. Delete it and use CSP.
Expect-CT — enforced Certificate Transparency reporting. Obsolete: CT is now mandatory for publicly trusted certificates, so the header does nothing. Delete it.
Public-Key-Pins — HPKP pinned specific public keys. Removed from browsers because it was dangerous: a misconfiguration or lost key could render a domain unreachable for the pin’s duration, and this happened to real sites. If you see it in a config, that config has not been reviewed in years.
Where to spend your time
If you do nothing else, deploy CSP properly and set your cookie attributes. Those two address the attack classes that actually compromise applications. HSTS and nosniff are cheap enough to add in the same change.
Everything below that is real but incremental, and none of it substitutes for the things headers cannot do: input validation, output encoding, authentication, and keeping dependencies patched. A perfect header score with an unpatched dependency is a site with a perfect header score — and prioritising which dependencies to patch first is its own problem, covered in our guide to EPSS and KEV.