The easiest security headers are winning because the easiest security headers are easy.
That sounds stupidly obvious, but it is the whole story.
If you look at real-world adoption data instead of blog posts, the gap between “one-line config” headers and “this will expose architectural debt” headers is enormous. Teams happily turn on X-Content-Type-Options. They often set X-Frame-Options. A fair number manage HSTS. Then the curve falls off a cliff when you ask for a real Content Security Policy or modern cross-origin isolation headers — anything that requires application changes instead of a server-snippet copy-paste.
The data is blunt about this.
What the Numbers Show
Large-scale web measurement projects like the HTTP Archive Web Almanac consistently paint the same picture. The numbers shift year over year, but the shape stays:
High adoption (40-50%+):
X-Content-Type-Options: nosniff— one line, no configuration, breaks nothing. The highest adoption of any security header.X-Frame-Options— also simple.DENYorSAMEORIGIN. Most frameworks add it by default.
Medium adoption (20-30%):
Strict-Transport-Security— higher than you’d expect, because cloud platforms and CDNs often set it. But many sites set shortmax-agevalues and skipincludeSubDomains, limiting the protection. HSTS preload adoption is a fraction of HSTS adoption.
Low adoption (10-15%):
Content-Security-Policy— the most important and least deployed. And of the sites that have it, a significant portion useunsafe-inline, which defeats the primary XSS protection CSP provides. Functional CSP — without unsafe-inline or unsafe-eval — is in the low single digits as a percentage of all sites.Referrer-Policy— growing but still far from universal.Permissions-Policy— newer header, patchy browser support history, very low adoption.
Very low adoption (<5%):
Cross-Origin-Opener-Policy,Cross-Origin-Resource-Policy,Cross-Origin-Embedder-Policy— the cross-origin isolation trio. Almost entirely limited to major tech platforms.
The CSP Gap Is the Story
Every other header is a configuration line. CSP is an engineering project.
Setting X-Content-Type-Options: nosniff takes two seconds and breaks almost nothing. Setting a functional CSP means auditing every script on your page, replacing inline scripts with nonces or hashes, controlling every resource origin, and testing extensively. For a site with years of accumulated frontend code, third-party analytics tags, embedded widgets, and marketing scripts, that’s weeks of work.
So teams do the rational thing: they set the easy headers, pass the scanner check, and defer CSP indefinitely.
The result is predictable. Sites proudly score 8/14 on header checks. The 8 they have are the ones that take five minutes to configure. The one that would actually prevent their most likely attack — XSS via script injection — is the one they skipped.
The unsafe-inline Problem
Even counting CSP adoption generously, the functional number is worse than it appears. A CSP that includes 'unsafe-inline' for scripts permits arbitrary inline script execution — which is exactly what XSS exploits. The policy exists. It just doesn’t protect against the primary attack it was designed to stop.
Why do sites use unsafe-inline? Because their application breaks without it. Inline event handlers, inline script tags in templates, third-party widgets that inject scripts — all of these fail under a strict CSP. Fixing them requires refactoring. Adding unsafe-inline requires one line.
The industry created a header so powerful that most sites can only deploy it in a mode that neutralizes its power.
HSTS: Closer But Not There
HSTS adoption looks better than CSP, partly because CDN providers like Cloudflare set it automatically for customers. But HSTS quality varies wildly.
A max-age=86400 (one day) provides minimal protection. A one-day max-age means an attacker only needs the victim to not visit the site for 24 hours before the HSTS protection expires. The recommended minimum is one year (31536000 seconds). Many sites fall far below that.
HSTS preload — submitting your domain to browser preload lists so HTTPS is enforced even on the very first visit — is a fraction of HSTS deployment. It requires includeSubDomains and a long max-age, plus an explicit submission. The commitment scares many domain owners.
The Zombie Headers
X-XSS-Protection is still present on a startling number of sites. Chrome removed its XSS Auditor in 2019. Firefox never had one. The header does nothing in any modern browser. Yet it persists in server configs, copied from blog posts written a decade ago and never cleaned up.
Its presence is almost a marker for “security by cargo cult” — someone pasted a header block from a 2015 tutorial and never revisited it.
What This Tells Us
Security header adoption follows a clear pattern: if it’s a one-line config change with no risk of breakage, adoption is decent. If it requires understanding your application, adoption collapses.
The headers that matter most — CSP and HSTS with preload — are exactly the ones that require the most effort. The headers that are easiest to deploy — nosniff, X-Frame-Options — provide the least marginal protection on a modern browser.
The average site is optimizing for scanner scores, not attack surface. A neat report with green checkmarks for the easy stuff and a quietly missing CSP.
If you’re looking at your own headers and everything is green except CSP, you haven’t done the hard part yet. The hard part is where the protection actually lives.