CSP Evaluator
Paste a policy and read what the browser will enforce, what it will ignore, and every weakness it leaves open — each finding with its CSP Level 3 clause.
Up to 32,768 characters. The text stays on this device.
This page reads only the text you paste. A static page on this origin cannot fetch your site's policy for you.
- 0 High
- 0 Medium
- 0 Low
- 0 Info
Paste a policy to see findings here.
What the severities mean
- High — the policy admits attacker-controlled script execution.
- Medium — a bypass avenue that needs one more condition.
- Low — a hardening gap.
- Info — the browser will ignore this.
| Severity | Directive | Evidence | CSP3 clause | Fix |
|---|---|---|---|---|
| Paste a policy to see findings here. | ||||
| Directive | Sources |
|---|---|
| Paste a policy to see findings here. | |
What the browser ignores
- Paste a policy to see findings here.
Nothing you paste here leaves this tab. State only ever travels in links you explicitly create.
Do it offline:$
How to use it
- Paste the policy. A Content-Security-Policy or Content-Security-Policy-Report-Only field name may stay on the front, and a comma-separated value is read as several policies.
- Say how the policy reaches the browser: a response header, or a meta http-equiv element. Delivery decides which directives are honoured at all.
- Read the counts first. They are four numbers, one per severity, with no total and no letter behind them.
- Work down the findings. Every row carries a severity label, the directive, the exact text it points at, the CSP Level 3 clause and — where one exists — the value to write instead.
- Copy the report as text or as JSON when you are done. The policy stays on this device: in the link fragment you share, and — unless it looks credential-shaped — in this browser's storage until you clear it.
Paste a Content-Security-Policy and this page reads it against the CSP Level 3 text: what a browser enforces, what it quietly discards, and where the policy still leaves script execution open. Each finding names its clause and, where a mechanical edit exists, the replacement value. There is no grade and no score — counts and a printed rubric instead. Only the text you paste is read.
Examples
default-src 'self'; script-src 'self' 'unsafe-inline' https:delivery header
disposition enforced
policies 1
counts high 2 · medium 1 · low 2 · info 0
1. high script-src 'unsafe-inline' §6.7.3.2
fix script-src 'self' https:
2. high script-src https: §6.7.2.8
fix script-src 'self' 'unsafe-inline'
3. medium base-uri (not set) §6.3.1
fix base-uri 'none'
4. low frame-ancestors (not set) §6.4.2
fix frame-ancestors 'none'
5. low form-action (not set) §6.4.1
fix form-action 'self'Nothing neutralises the inline keyword, so injected script runs and any host reachable over https serves more of it. Two rows, because removing one leaves the other standing.
script-src 'unsafe-inline' 'nonce-abc'delivery header
disposition enforced
policies 1
counts high 0 · medium 4 · low 2 · info 1
1. medium script-src 'nonce-abc' §7.1
2. medium default-src (not set) §6.1.3
fix default-src 'none'
3. medium object-src (not set) §6.1.9
fix object-src 'none'
4. medium base-uri (not set) §6.3.1
fix base-uri 'none'
5. low frame-ancestors (not set) §6.4.2
fix frame-ancestors 'none'
6. low form-action (not set) §6.4.1
fix form-action 'self'
7. info script-src 'unsafe-inline' §6.7.3.2
fix script-src 'nonce-abc'
ignored neutralized-source · 'unsafe-inline' §6.7.3.2A nonce in the same list makes the inline keyword inert, so it drops to informational and moves to the ignored region. The nonce itself is three characters, which is a finding of its own.
script-src 'unsafe-inline' https: 'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV' 'strict-dynamic'delivery header
disposition enforced
policies 1
counts high 0 · medium 3 · low 2 · info 2
1. medium default-src (not set) §6.1.3
fix default-src 'none'
2. medium object-src (not set) §6.1.9
fix object-src 'none'
3. medium base-uri (not set) §6.3.1
fix base-uri 'none'
4. low frame-ancestors (not set) §6.4.2
fix frame-ancestors 'none'
5. low form-action (not set) §6.4.1
fix form-action 'self'
6. info script-src 'unsafe-inline' §6.7.3.2
fix script-src https: 'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV' 'strict-dynamic'
7. info script-src https: §8.2
fix script-src 'unsafe-inline' 'nonce-DhcnhD3khTMePgXwdayK9BsMqXjhguVV' 'strict-dynamic'
ignored neutralized-source · 'unsafe-inline' §6.7.3.2
ignored neutralized-source · https: §8.2This is the compatibility policy printed in the spec. Older browsers read the scheme; newer ones honour the nonce and drop it. An expression the browser drops is listed once, as dropped, never a second time as a way in.
default-src 'self'; frame-ancestors 'none'; sandbox allow-forms; report-uri /csp-reportdelivery meta
disposition enforced
policies 1
counts high 0 · medium 1 · low 1 · info 4
1. medium base-uri (not set) §6.3.1
fix base-uri 'none'
2. low form-action (not set) §6.4.1
fix form-action 'self'
3. info frame-ancestors frame-ancestors 'none' §3.3
4. info sandbox sandbox allow-forms §3.3
5. info report-uri report-uri /csp-report §3.3
6. info report-uri report-uri /csp-report §6.5.1
ignored meta-ignored-directive · frame-ancestors 'none' §3.3
ignored meta-ignored-directive · sandbox allow-forms §3.3
ignored meta-ignored-directive · report-uri /csp-report §3.3From a response header those three lines would be enforced. In markup they do nothing, so the frame gap is not called a gap: there is no way to close it here.
script-src 'self' nonce-abcdef1234567890delivery header
disposition enforced
policies 1
counts high 0 · medium 4 · low 2 · info 0
1. medium script-src nonce-abcdef1234567890 §2.3.1
fix script-src 'self' 'nonce-abcdef1234567890'
2. medium default-src (not set) §6.1.3
fix default-src 'none'
3. medium object-src (not set) §6.1.9
fix object-src 'none'
4. medium base-uri (not set) §6.3.1
fix base-uri 'none'
5. low frame-ancestors (not set) §6.4.2
fix frame-ancestors 'none'
6. low form-action (not set) §6.4.1
fix form-action 'self'Unquoted, that token is a host name in the grammar, so the policy allows a server nobody owns and no inline script gets a pass. The fix is a pair of quotes.
default-src 'none'; script-src 'strict-dynamic' 'nonce-cnJhbmRvbTEyOGJpdHM0NTY3'; style-src 'self'; img-src 'self'; connect-src 'self'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'; report-to csp-endpointdelivery header
disposition enforced
policies 1
counts high 0 · medium 0 · low 0 · info 0Four zeroes mean no rule in the list below matched. Whether the nonce is fresh per response, and whether every script on the page actually carries it, is not visible in the text of a policy.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
A repeated directive is not a merge
When a name appears twice, the first occurrence is enforced and the second is discarded whole. The later line is reported as ignored rather than folded into the first.
Quotes are what make a nonce a nonce
Nonce and hash expressions are quoted in the grammar. Drop the quotes and the token becomes an ordinary host name — still valid, still parsed, and protecting nothing at all.
Two policies in one field both apply
A comma splits a header value into separate policies. Each is read and counted on its own here, and loosening one of them loosens nothing.
Absent is judged on the directive that applies
Fetch directives fall back to default-src, so a policy is never told it is missing object-src when default-src covers it. base-uri, form-action and frame-ancestors have no fallback at all.
Report-only enforces nothing
A report-only policy blocks no load. The disposition detected in your paste is shown, and one with nowhere to send reports is itself a finding.
Long policies are refused, not trimmed
Past 32,768 characters nothing is evaluated. Half a policy produces confident nonsense, so the reading stops and says so instead of reporting on the part that fitted.
Common errors
No finding in the declared list — that is not a claim that the site is safe.- The list is closed and public, and every rule in it cites a clause. A policy can pass all of them and still be wrong for the page it protects, so the wording says what was checked.
This policy is 32,769 characters — over the 32,768-character limit for this tool. Nothing was evaluated: half a policy produces confident nonsense. Paste the Content-Security-Policy field on its own, without the surrounding response.- A real policy field is a line or two. A paste this large is usually a whole response or a log, and the count in the message is what arrived.
info script-src 'unsafe-inlin' §2.3.1- A quoted token outside the grammar matches nothing, so a misspelt keyword narrows the policy instead of widening it. Informational, because it admits nothing.
info foo-bar foo-bar 'self' §2.2.1- The parser skips an unrecognised name entirely. That is how a directive can look present in the header and be absent from the policy a browser enforces.
FAQ
Does this read the policy from my own site?
No. It reads the text in the box and nothing else. This page is static and makes no request to your origin, so fetching a live header is not something it can do; copy the field out of your response and paste it here.
Why is there no grade or score?
Because a single letter hides its own weighting. You get four counts and the rubric that produced them: severity is fixed per finding, so the same policy always reads the same way and you can see exactly which rule pushed a number up.
Why is 'unsafe-inline' sometimes only informational?
Because a nonce or a hash in the same source list makes browsers ignore it. The keyword is then dead text rather than a hole, so it is reported as ignored — and it stays high the moment the nonce or hash goes away.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Copy the findings report.