Tools
CSP Evaluator & Builder
Paste a Content-Security-Policy header, a bare policy or a <meta> tag and get a grade with specific findings. Or build a strict policy and copy it as a header for nginx, Express or Cloudflare. Everything runs in your browser.
Runs in your browser. Nothing is sent anywhere.
Result
Score 0 / 100
The grade is a heuristic: high findings cap it at D. It checks the policy text only, not whether your site works under it.
Findings
- High
script-src'unsafe-inline' in script-src without nonce or hash
Inline <script> blocks and event-handler attributes are allowed, which is exactly what an XSS payload uses. This effectively disables CSP script protection.
Fix: Add a per-response nonce to your script tags and use script-src 'nonce-…' 'strict-dynamic'; or hash static inline scripts.
Read more in the CSP reference - High
script-srcdata: in script-src
An attacker who can inject a script tag can use <script src="data:text/javascript,..."> to run arbitrary code.
Fix: Remove data: from script sources.
- High
default-srcdefault-src is not 'none'
Plugin content from any host can be embedded, which has been a way to execute script despite a strict script-src.
Fix: Add object-src 'none'.
- Medium
script-src'unsafe-eval' in script-src
Allows eval(), new Function() and string-form setTimeout. If an attacker controls any string that reaches those sinks, they get script execution. Some frameworks and bundlers need it in dev only.
Fix: Remove it in production. For WebAssembly only, use the narrower 'wasm-unsafe-eval'.
- Medium
script-srchttps://ajax.googleapis.com in script-src is a risky allowlist entry
ajax.googleapis.com hosts old AngularJS builds, which can be abused as a script gadget when the page has an injection point; other *.googleapis.com endpoints have served JSONP callbacks. Allowlisting a broad host like this has been part of published CSP bypasses, so treat it as a risk, not as proof that the page is exploitable.
Fix: Prefer nonce or hash with 'strict-dynamic', or pin the exact file with a hash or SRI.
- Medium
script-srchttps://cdnjs.cloudflare.com in script-src is a risky allowlist entry
cdnjs hosts many library versions, including AngularJS and other libraries with known script gadgets. Allowlisting the whole host makes those usable by an attacker. Allowlisting a broad host like this has been part of published CSP bypasses, so treat it as a risk, not as proof that the page is exploitable.
Fix: Prefer nonce or hash with 'strict-dynamic', or pin the exact file with a hash or SRI.
- Medium
default-srcdefault-src is very permissive
Every fetch directive you did not list explicitly (images, fonts, frames, workers, objects...) falls back to this broad source list.
Fix: Start from default-src 'self' or 'none' and open specific directives.
- Medium
frame-ancestorsMissing frame-ancestors
frame-ancestors controls who may embed your page, so it is the clickjacking defence. It does not fall back to default-src. Without it, only the legacy X-Frame-Options header can protect you.
Fix: Add frame-ancestors 'none' (or 'self').
Read more in the CSP reference - Low
script-src'self' in script-src
'self' trusts every file on your origin. If any endpoint on it reflects a callback parameter (JSONP) or serves user-uploaded JavaScript, it becomes a bypass. It is a reasonable baseline for static sites; with a nonce and 'strict-dynamic' CSP3 browsers ignore it.
- Low
style-src'unsafe-inline' in style-src
Inline styles allow CSS-based data exfiltration and UI redressing after an injection, but cannot run script. Many sites accept this trade-off for style attributes; it is far less severe than the same keyword in script-src.
- Low
base-uriMissing base-uri
base-uri does not fall back to default-src. Without it, an injected <base href> can redirect every relative URL, including relative script paths, to an attacker's server.
Fix: Add base-uri 'none' (or 'self' if you use <base>).
Read more in the CSP reference - Low
form-actionMissing form-action
form-action does not fall back to default-src. An injected <form> (or a hijacked button) can post credentials to any host.
Fix: Add form-action 'self' (list other hosts you submit to).
- Note
report-urireport-uri is deprecated in favour of report-to
report-uri still works in Chrome, Firefox and Safari. report-to (the Reporting API) is the successor but is not supported everywhere, so policies commonly send both. report-uri is ignored when report-to is supported.
Read more in the CSP reference - Note
upgrade-insecure-requestsNo upgrade-insecure-requests
Optional. It rewrites http:// subresource URLs to https:// before fetching, which helps when migrating an old site to HTTPS.
Parsed directives
| Directive | Sources |
|---|---|
default-src | * |
script-src | 'self''unsafe-inline''unsafe-eval'https://ajax.googleapis.comhttps://cdnjs.cloudflare.comdata: |
style-src | 'self''unsafe-inline' |
report-uri | /csp-report |
What is not set, and what covers it
Fetch directives you leave out fall back along a fixed chain, ending at default-src. A few directives never fall back, so leaving them out means no restriction at all.
| Directive | Covered by |
|---|---|
child-src | default-src |
connect-src | default-src |
fenced-frame-src | default-src(chain: frame-src > child-src > default-src) |
font-src | default-src |
frame-src | default-src(chain: child-src > default-src) |
img-src | default-src |
manifest-src | default-src |
media-src | default-src |
object-src | default-src |
script-src-attr | script-src(chain: script-src > default-src) |
script-src-elem | script-src(chain: script-src > default-src) |
style-src-attr | style-src(chain: style-src > default-src) |
style-src-elem | style-src(chain: style-src > default-src) |
worker-src | script-src(chain: child-src > script-src > default-src) |
base-uri | Never falls back to default-src. Unrestricted until you set it. |
form-action | Never falls back to default-src. Unrestricted until you set it. |
frame-ancestors | Never falls back to default-src. Unrestricted until you set it. |
sandbox | Never falls back to default-src. Unrestricted until you set it. |
No fallback: base-uri, form-action, frame-ancestors, sandbox, report-uri, report-to, upgrade-insecure-requests, require-trusted-types-for, trusted-types.
How to read the result
Severity reflects how much a finding weakens protection against script injection, not whether a site is exploitable. An allowlisted host such as cdnjs.cloudflare.com is listed as a risk because published bypasses have used script gadgets on shared hosts. It is not proof that your page has an injection point.
The fastest route to a strong policy is a per-response nonce with 'strict-dynamic', plus object-src 'none' and base-uri 'none'. Roll it out with Content-Security-Policy-Report-Only first. The full background is in the Content-Security-Policy header reference. To check the headers a live site sends, use the Header Inspector or the Site Verifier.
Frequently asked questions
What does this CSP evaluator check?
It parses the policy and flags unsafe-inline and unsafe-eval in script-src, wildcard, https: and data: sources, missing object-src, base-uri and frame-ancestors, 'strict-dynamic' misuse, allowlisted hosts with known bypass risk, typos, unknown and duplicate directives, and deprecated reporting directives. It reads the policy text only; it cannot tell whether your pages still work under it.
Why does unsafe-inline not matter when I also have a nonce?
In browsers that support CSP2 and CSP3, 'unsafe-inline' is ignored in a source list that also contains a nonce or a hash. Keeping it only helps ancient CSP1 browsers. Without a nonce or hash, 'unsafe-inline' allows every inline script, which removes most of the protection against XSS.
Which directives fall back to default-src?
Fetch directives such as script-src, style-src, img-src, connect-src, font-src, media-src, object-src, manifest-src, frame-src and worker-src fall back to default-src when you omit them (frame-src and worker-src check child-src first, worker-src also script-src). base-uri, form-action, frame-ancestors and sandbox never fall back, so a default-src alone leaves them unrestricted.
Can I set frame-ancestors in a meta tag?
No. Browsers ignore frame-ancestors, report-uri and sandbox in a <meta http-equiv="Content-Security-Policy"> tag. To prevent clickjacking you must send the policy as an HTTP response header.
Is my policy sent to a server?
No. Parsing, grading and generation run entirely in your browser. A share link stores the policy in the URL hash, which the browser does not send to any server.