CSP evaluator for Content-Security-Policy headers
This CSP evaluator parses a Content-Security-Policy and points out the settings that weaken it. A CSP is only as strong as its loosest directive, and small mistakes, like an unquoted keyword or a trusted CDN, can quietly turn it into a policy that blocks nothing an attacker cares about. The tool lists every directive it found, then ranks findings by severity so you know what to fix first.
What is a Content Security Policy?
A CSP is an HTTP response header (or a <meta> tag) that tells the browser which sources of scripts, styles, images, frames and connections a page may use. Its main purpose is to limit the damage of cross-site scripting (XSS): even if an attacker injects markup, the browser refuses to run scripts that the policy does not allow. It also controls framing, form targets and mixed content.
Example
The sample policy looks reasonable at first glance:
default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src * data:; connect-src 'self' https://api.example.com; report-uri /csp-report
The evaluator's findings tell a different story:
✘ HIGH script-src allows 'unsafe-inline' — inline <script> and event handlers run, which defeats most XSS protection.
! MEDIUM script-src trusts a public CDN; known JSONP/Angular gadgets on such hosts can bypass the policy. Prefer nonces + 'strict-dynamic'.
! MEDIUM object-src is not 'none' — plugins (<object>, <embed>) could execute script. Add object-src 'none'.
! MEDIUM base-uri is missing — an injected <base> tag can hijack relative script URLs. Add base-uri 'self' or 'none'.
• INFO frame-ancestors is missing — the page can be framed (clickjacking) unless X-Frame-Options is set.
• INFO form-action is missing — injected forms could post data to other origins.
Because 'unsafe-inline' is present without a nonce or hash, an injected <script> tag or onerror attribute would simply run. Additional INFO lines cover the inline styles and the deprecated report-uri.
What the evaluator checks
- Script sources –
'unsafe-inline'without a nonce or hash,'unsafe-eval', broad sources like*,https:,data:andblob:, wildcard subdomains, and public CDNs that host bypass gadgets. - Fallbacks – when
script-srcis absent,default-srcis used, just as the browser does. With neither, the policy offers no script protection. - Nonce and strict-dynamic – a nonce or hash combined with
'strict-dynamic'is reported as the recommended strict CSP;'strict-dynamic'without a nonce is flagged because it blocks every script. A nonce shorter than 8 characters gets a warning, and any nonce brings a reminder that it must be random and new for every response. - Hardening directives – missing
object-src 'none',base-uri,frame-ancestorsandform-action. - Broad sources elsewhere –
*orhttps:in a directive other than the script one, such asconnect-src,frame-src,base-uriorform-action, is listed as INFO because it restricts very little.img-src,media-srcandfont-srcare left out of this check. - Syntax mistakes – unquoted keywords (
selfinstead of'self'is treated as a host name), duplicate directives (only the first counts), unknown directive names,'none'mixed with other sources, andhttp:sources. - Reporting – no reporting endpoint, or
report-uriwithout the newerreport-to.
Fixing common findings
The most robust modern pattern is a nonce-based strict policy:
script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none'
Your server generates a fresh random nonce for every response and adds it to each legitimate <script> tag. Scripts those trusted scripts load are allowed through 'strict-dynamic', so you do not need to maintain host allowlists. For static sites, use 'sha256-…' hash sources instead. They hold the Base64 SHA-256 digest of the exact inline script, the same format the SRI hash generator prints when set to SHA-256, so you can paste the script body there and copy the value. The same tool gives third-party files an integrity attribute.
A CSP is defense in depth, not a replacement for output encoding. Keep escaping user data with tools like HTML escape so injection does not happen in the first place.
Report-only mode
You can paste a Content-Security-Policy-Report-Only header too. Deploying a new policy in report-only mode first lets you collect violations from real traffic without breaking pages, then switch to enforcement once reports are clean.
Privacy
The policy is parsed and evaluated entirely in your browser. Nothing is fetched from your site and nothing you paste is uploaded.