Our CSP adoption number was measuring the wrong thing, and here is the corrected one
In the September 2026 census, 21,421,283 domains sent a Content-Security-Policy header of some kind. That’s 10.01% of http_headers_present, the 214,006,060 domains that sent a Server, CSP or X-Powered-By header. If you’ve cited a CSP adoption figure from this site, that presence count is the one it came from.
Of those 21,421,283 policies, 5,324,566 carry a default-src or a script-src directive. That’s 24.86% of csp_present and 2.49% of http_headers_present. This is the number you meant when you cited CSP adoption, and it’s the one this page now stands behind.
Both figures come from the same edition and the same responses, and neither has moved; they sit apart because they answer different questions, and the earlier published figure answered the wrong one.
Which measure we used, and which we should have used
The figure this site published before counted header presence. The census fetched each domain over HTTP, looked at the response, and asked whether a Content-Security-Policy header was in it. If yes, the domain counted. That’s a fair thing to count, and it’s the cheap one, a string match on a header name, which is why the earlier figure on this site used it.
The trouble is what a reader does with it. “Ten percent of sites use CSP” gets read as “ten percent of sites restrict where script can load from”, because restricting script is what CSP is for. Header presence doesn’t tell you that. A policy that says only upgrade-insecure-requests is a CSP header, and so is a policy that says only frame-ancestors 'none', and neither of them says anything about script.
The measure we should have used, and now use, is the directive count: does the policy carry a default-src or a script-src? Those are the two directives that govern where script may load from, and a policy with neither leaves script unconstrained. The old value retires the day this page ships. It’s gone from every page on this site, and the build now fails if it reappears. If you hold a citation to it, this is why it moved.
Number one: the header was there
Header presence is a yes-or-no on the response. The census counts a domain in csp_present if the response carried a Content-Security-Policy header with any value at all. The value isn’t inspected at this stage; the count is of the header name.
The denominator matters as much as the numerator, and it’s narrower than “websites” or “the internet”. It’s http_headers_present: domains that answered an HTTP request with at least one of a Server, Content-Security-Policy or X-Powered-By header. Domains that didn’t answer, or answered without any of those headers, aren’t in it. Quote the figure as 21,421,283 of 214,006,060, or as 10.01% of http_headers_present, and a reader can check it against the September 2026 census whenever they like.
Number two: the policy reaches script
The second count starts from the same 21,421,283 policies and reads the directives. A policy is in this count if it contains default-src or script-src. These are the two directives that can constrain where script loads from: script-src does it directly, and default-src is the fallback the browser applies to script when script-src is absent.
That count is 5,324,566. Against csp_present it’s 24.86%, the share of policies that say anything about script. Against http_headers_present it’s 2.49%. The second denominator is the one to quote for adoption, because the question behind an adoption figure is how many domains constrain script, and 2.49% of http_headers_present is the answer to that question.
Three example headers show where the line falls.
Content-Security-Policy: upgrade-insecure-requests
This one is in the first count only. It tells the browser to rewrite plain HTTP fetches to HTTPS and says nothing about script.
Content-Security-Policy: frame-ancestors 'self'
Also first count only. It stops other sites framing the page, which is worth having, and it leaves script unconstrained.
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-r4nd0m'
This is in both counts. Script may load from the page’s own origin or carry the matching nonce, and nothing else runs.
What fills the three quarters
A policy with one directive solves one problem, mixed content warnings or clickjacking, and once it’s deployed a header-presence counter scores it as CSP adoption, whatever it was put there for. The statistic then reports something the policy never claimed.
Hosting platforms and frameworks add to this. A platform that emits upgrade-insecure-requests on each response it serves puts a CSP header on millions of domains whose owners have no idea it’s there. That’s fine for those domains and misleading for the statistic, and the directive count leaves them out because a platform default of that kind carries no default-src or script-src.
There’s also the honest case of a site that tried a script-restricting policy, broke its own pages, and fell back to a directive that couldn’t break anything. That site shows up in the first count as an adopter and in the second count as what it is, a site without a script policy.
The test for your own header
Fetch your own front page headers and look for the policy:
curl -sI https://yourdomain.example | grep -i content-security-policy
Read the value. If it contains default-src or script-src, your policy constrains script and belongs with the 5,324,566. If it contains neither, it’s in the three quarters that don’t reach script, however long the rest of the header is. The fix starts with adding default-src 'self' as the first directive, loading the site with the browser console open, and adding sources back one at a time as the console names them. The free scan below reads the same header and tells you which side of the line your domain is on.
The sentence to paste
People copy the number and leave the denominator behind, and that’s how the earlier figure went wrong once it left this site. Paste this one whole:
In the September 2026 defaults.exposed census, 5,324,566 domains sent a Content-Security-Policy header carrying a default-src or script-src directive, 2.49% of the 214,006,060 domains that sent a Server, CSP or X-Powered-By header (http_headers_present).
If you need the presence figure as well, keep it separate and label it for what it is:
21,421,283 domains sent a Content-Security-Policy header of any kind, 10.01% of http_headers_present; 24.86% of those policies constrain script.
Don’t write that adoption fell from one to the other, because it didn’t: both measurements are from September 2026, and a writer who shows a drop has told the reader the two numbers are the same measure, which they aren’t. Neither figure is comparable with an earlier edition’s either; each census is quoted on its own.
Reading any adoption statistic
The same trap sits under most header statistics, and it’s worth carrying the two-number habit to the rest of them. The headers pillar covers the other response headers the census reads. HSTS has its own version of the presence-versus-effect gap, since a header can be present with a max-age too short to protect anyone. TLS versions are easier to count without this trap, because a handshake either negotiated a version or it didn’t, and there’s no directive inside it to misread.
Before you cite a CSP figure from anywhere, including here, ask two things of the source: which measure it used, and which denominator it named, with the edition date attached to both. If the source can’t answer, don’t cite the figure. Then run the header test above on your own domain, so that the page you cite from carries a default-src or script-src itself.
Figures as of 5 September 2026, from the September 2026 edition of the defaults.exposed census. Census numbers move every month; the current values are on the census data page.