Build a Content Security Policy and see what a browser will actually apply, including the CSP3 rules that silently discard what you wrote.
Initializing in your browser…
Build a Subresource Integrity attribute, with more than one hash if you want, and check an existing one against the file it is meant to pin.
Generate passwords from the Web Crypto generator by rejection sampling, with the entropy stated exactly from the alphabet, the cost of every rule computed, and crack times against four named attacker models
Decode an X.509 certificate field by field, check which host names it covers, and verify that each certificate in a chain was signed by the one above it
Your policy uses 'unsafe-inline' for a handful of inline scripts, and you are moving to a nonce so it can be removed. You add the nonce first, intending to drop 'unsafe-inline' afterwards.
The policy you wrote
script-src 'unsafe-inline' 'self' 'nonce-8emk/+h69qBbrg9F+Fbtaw=='
What a browser will actually apply
script-src 'self' 'nonce-8emk/+h69qBbrg9F+Fbtaw=='
script-src 'unsafe-inline' in script-src is IGNORED, because this
directive also carries a nonce or a hash. CSP Level 3 section
6.7.3.2 discards it whenever either is present. Every inline
script or style here must carry the nonce or match a hash, or
it will be blocked. Keeping 'unsafe-inline' is still worth
doing as a fallback for CSP Level 1 browsers, which ignore the
nonce instead.Adding the nonce did not widen the policy; it narrowed it, and it narrowed it before you removed anything. CSP Level 3 discards `'unsafe-inline'` outright whenever a nonce or a hash appears in the same directive, so at the moment the nonce goes in, every inline script that has not yet been given the nonce attribute stops running. Nothing in the policy text says so, no directive was removed, and the deployment looks like a widening.
The project tests do not take this from the specification. They serve a real page under each policy and record what ran: with `'unsafe-inline'` alone the inline script runs, and with a nonce beside it the same script is blocked while the nonced one runs. The same method covers `'strict-dynamic'` discarding `'self'`, a bare `self` allowing nothing where `'self'` allows the same-origin script, `frame-ancestors` being ignored in a meta element, and a report-only policy blocking nothing at all.
The advice that follows from it is the part worth keeping: do not remove `'unsafe-inline'` when you add the nonce. CSP Level 1 browsers ignore the nonce and fall back to `'unsafe-inline'`, while CSP Level 3 browsers ignore `'unsafe-inline'` and honour the nonce, so leaving both in place is what makes one policy work in both.
Build a Content Security Policy directive by directive, and see what a browser will actually apply. Several CSP Level 3 rules discard parts of a policy silently, so the policy you wrote and the policy that runs are not always the same thing; this tool shows both, and every rule it applies is checked against Chrome itself.
A Content Security Policy is a list of directives, each naming where a kind of resource may come from. The difficulty is not writing one; it is that several of the rules that decide what a policy does are applied silently, so a policy can look strict and do nothing, or look permissive and break the site.
The rule most builders miss: **a nonce or a hash makes 'unsafe-inline' be ignored.** CSP Level 3 section 6.7.3.2 discards 'unsafe-inline' entirely whenever the same directive carries a nonce or a hash source. Add a nonce to a policy that was relying on 'unsafe-inline' and every inline script without the nonce stops running, with nothing in the policy text to suggest it. This tool shows the written policy and the effective one side by side whenever they differ, and the project tests confirm the behaviour by serving a page under the policy and watching whether Chrome runs the script, rather than by quoting the specification. 'strict-dynamic' is handled the same way: it discards every host source, every scheme source and 'self' in its directive, which the tool reports by name and count.
**Some directives have no default-src fallback.** frame-ancestors, form-action, base-uri, report-uri and sandbox are not covered by default-src, however strict it is, so leaving them out leaves them unrestricted. Each is labelled in the interface and each is reported when missing, with what it costs: any site can frame the page, an injected form can submit anywhere, an injected base element can redirect every relative URL. **Four directives are ignored in a meta element:** frame-ancestors, report-uri, report-to and sandbox. A builder that hands you a meta tag containing them is handing you something that does less than it says, so choosing meta delivery here flags them as errors, which is exactly what Chrome reports in the console.
**A keyword without its quotes is a host name.** 'self' is the keyword; self is a domain, and a policy containing the second is syntactically valid, allows nothing, and reports no error anywhere. Typing an unquoted keyword into a source box corrects it rather than passing it through, and the difference is measured in the tests: a bare self allows nothing, while the corrected 'self' allows the same-origin script.
A nonce is generated for each build from the platform generator, 128 bits as the specification asks, with the reminder that a server has to produce a new one on every response. Inline scripts and styles can be hashed to sha256, sha384 or sha512 and added directly to a directive, which removes the need for 'unsafe-inline' without needing a nonce at all. The policy comes out as an HTTP header, an nginx add_header with the always flag, an Apache Header always set, an Express call, a Netlify _headers block, and a meta element when meta delivery is chosen. Report-only is a first-class mode with the right header name, and the tool points out that a report-only policy with no report-uri blocks nothing and records nothing.
Four presets are offered. The strict one is the shape the specification itself recommends, a nonce with 'strict-dynamic', which is the only arrangement that survives a page adding scripts at runtime without listing every CDN it might reach. A report-only preset is included because turning a policy straight on is how a site breaks: the way to find out what a real site does is to watch it first.
Start with the report-only preset, deploy it, and read the violation reports before switching to enforce.
The tool shows that adding the nonce makes unsafe-inline stop applying, which is the step that breaks inline scripts that were not updated.
Between strict-dynamic discarding host sources, a nonce discarding unsafe-inline, and a keyword missing its quotes, most silent CSP failures are one of three things, and each is named here.
frame-ancestors is the replacement, has no default-src fallback, and does nothing in a meta tag. All three are stated on the directive itself.
Because a nonce makes 'unsafe-inline' be ignored. CSP Level 3 section 6.7.3.2 discards it whenever the directive also has a nonce or a hash, so every inline script now needs the nonce attribute. Keeping 'unsafe-inline' in the policy is still worth doing as a fallback for CSP Level 1 browsers, which ignore the nonce instead, and the tool says so rather than telling you to remove it.
frame-ancestors has no default-src fallback. However strict default-src is, leaving frame-ancestors out leaves framing unrestricted. The same is true of form-action, base-uri, report-uri and sandbox.
Mostly. frame-ancestors, report-uri, report-to and sandbox are ignored in a meta element, and a report-only policy in a meta element is ignored entirely. Choose meta delivery here and those are flagged, which is the same thing Chrome prints in the console.
It says to trust scripts loaded by an already-trusted script, and in exchange it discards every host source, scheme source and 'self' in the same directive. It only does anything alongside a nonce or a hash. The tool lists what it discarded so the effect is visible rather than surprising.
A nonce if your server renders the page and can put a fresh value on every response. A hash if the inline block never changes, since it is static and needs no server involvement. A hash covers the exact bytes between the tags, whitespace included, so a formatter run will break it.
Only when what you typed would not do what you meant. A keyword without quotes is a host name to a browser, so typing self gives 'self'. A policy containing a bare self is valid, allows nothing, and produces no error at all, which makes it a hard thing to find on your own.
No. It is a second line of defence for when the first one fails. A policy with 'unsafe-inline' in script-src provides very little, and one with 'unsafe-eval' or a wildcard script-src provides essentially none; the tool says so where it applies. Escaping output correctly is still the first job.
This runs as client-side JavaScript. Keys, tokens, payloads, and other inputs never leave your device.