unsafe-inline on 27 homepages: 10 of the 13 policies that name script sources allow it anyway
unsafe-inline is the Content-Security-Policy keyword that exempts a page's own inline scripts from the source list above it. Across 27 homepages read on 2026-09-10, 18 send a policy, 13 name script sources, and 10 of those 13 carry the keyword — while zero pair it with a nonce, the combination where the spec says it stops applying.

FIELD TEST · 2026-09-10 · 30 domains · 27 readable · one pass
Sample and method: the same 30-site panel in use since 2026-08-15, each homepage requested once on 2026-09-10 with a desktop Chrome user agent, redirects followed, no JavaScript executed. We read the response headers of the final URL only and never the page body. Three domains answered 403 to our client — canva.com, medium.com and stackoverflow.com — leaving 27.
Eighteen of those 27 send a Content-Security-Policy. Thirteen of the 18 name where scripts may come from, and ten of those thirteen include 'unsafe-inline', the keyword that lets any inline script in the page run regardless of the rest of the list. Eight of the ten also allow 'unsafe-eval'. Nobody on this panel uses 'strict-dynamic', and nobody pairs unsafe-inline with a nonce.
How we measured this
One request per homepage, from outside, with no account anywhere. Everything below comes from two response headers on the final URL after redirects: Content-Security-Policy and Content-Security-Policy-Report-Only. We split each policy on semicolons, took the first token of each part as the directive name and the rest as its source list, then counted keywords by exact match inside the list.
# The whole measurement for one domain
curl -sIL -A 'Mozilla/5.0 (Macintosh) Chrome/126.0' https://example.com/ \
| grep -i '^content-security-policy'
# Just the script sources
curl -sIL -A 'Mozilla/5.0 (Macintosh) Chrome/126.0' https://example.com/ \
| tr ';' '\n' | grep -i 'script-src'
Three limits, and the first one decides how the counts can be read. A policy can also arrive in a <meta http-equiv> element in the HTML, and we never opened the body, so a site counted here as sending nothing may still have a policy we did not see. Second, a homepage is one URL: several of these sites almost certainly ship a different policy on their application or checkout pages, and this measurement says nothing about those. Third, keywords were matched literally, so a policy that includes 'unsafe-inline' inside a directive we did not read is not in these numbers.
There is a fourth thing we cannot settle from outside, and it is the one an SEO reader will want. Google's JavaScript SEO basics says that "Google Search runs JavaScript with an evergreen version of Chromium" and that "a headless Chromium renders the page and executes the JavaScript" (read 2026-09-10). Whether that renderer enforces a Content-Security-Policy the way a user's browser does is not stated on that page, and the word does not appear on it or on the companion troubleshooting page at all. We are not going to guess.
Nine of 27 homepages send no policy at all
The header is common but far from universal, and its absence is not correlated with size or with how technical the audience is. Two of the 18 senders also ship a report-only policy alongside the enforced one; nobody sends report-only on its own.
| Response carries | Sites (of 27) |
|---|---|
| Enforced policy | 18 |
| — and report-only too | 2 |
| Report-only alone | 0 |
| No policy header | 9 |
The nine without one are bbc.com, framer.com, about.gitlab.com, netlify.com, react.dev, shopify.com, slack.com, supabase.com and wikipedia.org. The two that also send report-only are mozilla.org and webflow.com — the pattern of a team testing a stricter policy in public before turning it on.
Length is the first hint that "has a CSP" is not one thing. Four of the 18 headers are under 130 characters; railway.com and wired.com send 22 characters each. At the other end notion.com sends 7,781 characters, and its script source list alone has 92 entries.
Five of the 18 policies say nothing about scripts
A policy only restricts what it names. Five of the 18 declare no script-src — and no default-src for it to fall back to, which means scripts are not covered at any level. Four of those five consist of a single directive, frame-ancestors, which is about who may put your page in a frame and has nothing to do with what runs on it.
| Directive | Policies (of 18) |
|---|---|
| img-src | 14 |
| style-src | 14 |
| script-src | 13 |
| frame-ancestors | 13 |
| default-src | 12 |
| connect-src | 11 |
| font-src | 11 |
| media-src | 10 |
| upgrade-insecure-requests | 8 |
| frame-src | 8 |
| base-uri | 5 |
| report-uri or report-to | 3 |
That frame-ancestors column has a history on this panel: we counted the same directive against the older header it replaces in x-frame-options across 27 homepages three weeks ago, and five sites were contradicting themselves. Here it matters for a smaller reason. Four sites ship a header called a Content-Security-Policy whose entire content is a clickjacking rule, and a scanner that reports "CSP present" on those four is telling you almost nothing.
unsafe-inline is in 10 of the 13 policies that name script sources
This is the finding. Of the 13 policies that bother to list script sources, ten include the keyword that switches inline scripts back on. Eight of those ten add 'unsafe-eval'. Only three restrict inline script at all, and each does it a different way.
| Site | Sources | unsafe-inline | unsafe-eval |
|---|---|---|---|
| notion.com | 92 | yes | yes |
| nextjs.org | 28 | yes | no |
| cloudflare.com | 25 | yes | yes |
| vercel.com | 21 | yes | yes |
| mozilla.org | 14 | yes | yes |
| stripe.com | 13 | no — hashes | no |
| figma.com | 11 | yes | yes |
| nytimes.com | 6 | yes | yes |
| arstechnica.com | 5 | yes | yes |
| techcrunch.com | 5 | yes | yes |
| news.ycombinator.com | 5 | yes | no |
| github.com | 1 | no | no |
| reddit.com | 1 | no — nonce | no |
Read the two ends of that table together. github.com and reddit.com each allow exactly one script source and neither keyword; stripe.com allows 13 sources and gates inline blocks on hashes. Everyone else wrote a source list and then added the one value that makes inline blocks exempt from it. techcrunch.com goes furthest: its five script sources include a bare *, alongside both keywords.
Styles are looser still. Fourteen of the 18 policies allow inline styles, either in style-src or through the default-src that style-src falls back to. That one is closer to unavoidable — a large share of component frameworks emit inline style attributes — and it is the reason a first CSP is usually written with the style rule already surrendered.
What the spec says the keyword does, and when it stops applying
The Content Security Policy Level 3 specification is precise about this, and it is worth reading the sentence rather than the summary. Inline script "will be blocked unless every policy allows inline script, either implicitly by not specifying a script-src (or default-src) directive, or explicitly, by specifying 'unsafe-inline', a nonce-source or a hash-source that matches the inline block" (CSP Level 3, read 2026-09-10).
The part most people miss is the interaction. The same document lists 'unsafe-inline' 'nonce-abc' among the "source lists that do not allow all inline behavior due to the presence of nonces and/or hashes" — a nonce anywhere in the directive turns the keyword off. That combination is how you deploy a strict policy without breaking old browsers, and it appears zero times on this panel. The three sites that restrict inline script do not send the keyword at all.
A source list plus 'unsafe-inline' is a list of where scripts may come from, followed by an exemption for the scripts already in the page.
The spec's own security section says what to do instead, in the same flat voice: "nonces provide a substantial improvement over 'unsafe-inline' when layering a content security policy on top of old code. When considering 'unsafe-inline', authors are encouraged to consider nonces (or hashes) instead." Ten of thirteen have not.
What this changes for you today
If you have ever been told that your Content-Security-Policy might be why a crawler renders your page as an empty shell, this measurement narrows the question a lot. A policy carrying unsafe-inline and a wide source list is unlikely to be blocking anything, because it barely blocks anything at all. Read your own header before you go looking further.
- Print your policy first and check whether
script-srcis even in it — five of 18 here restrict scripts at no level - If you are adding a nonce, remove
'unsafe-inline'in the same change; leaving both is a policy that reads strict and behaves loose in old browsers only - Do not treat "CSP present" from a scanner as a finding — four headers here are 130 characters of clickjacking rule
- Do not blame a permissive policy for a page that renders empty to crawlers; check what the raw HTML contains first
The empty-shell case has a different cause more often than not, and it is measurable from the same single request. Our tool for it is the AI crawler accessibility check, and the method behind the header side of it is written up in content security policy.
Common questions
How did you measure this?
One request per domain on 2026-09-10, desktop Chrome user agent, redirects followed, no JavaScript. Only the Content-Security-Policy and Content-Security-Policy-Report-Only headers on the final URL were read; the page body was never parsed. Twenty-seven of 30 domains answered 200. The two commands in the method section reproduce the per-site numbers.
Is unsafe-inline actually dangerous?
That depends on whether the page has an injection point, which is not something a header tells you. What the header does tell you is that the source list stops applying to inline blocks, so a policy with the keyword is not the defence its length suggests. We measured presence, not exploitability.
Does a Content-Security-Policy affect how Google indexes my page?
We do not know, and we could not find a published answer. Google says its renderer is an evergreen Chromium, and Chromium enforces these policies for users. Neither of Google's two JavaScript pages mentions Content-Security-Policy once, so the step from "it is a browser" to "it enforces your policy" is one we are leaving unmade.
Why do so many sites ship unsafe-inline?
The file cannot tell us, and neither can we. The shape of the data points at policies written after the page existed rather than before — you inherit a page full of inline analytics snippets and event handlers, and the keyword is the only value that ships without breaking it. But we cannot see anybody's deploy history, so that stays a reading.
What should a first policy look like?
Report-only, on the pages you actually care about, before anything is enforced. Two sites here are visibly doing that. It costs one header, blocks nothing, and gives you the violation list you would otherwise be guessing at.


