Check or build a Content Security Policy header

Paste a CSP header or meta tag to see each problem with a severity and a fix, plus the sources that really apply after fallback. Or build a policy and copy it for nginx, Apache or HTML.

How it works

  1. Paste a header value, a Content-Security-Policy line or a meta tag
  2. Read the findings, sorted from High to Info, and the effective policy table
  3. Or switch to Build, tick what your site needs, and copy it as a header, nginx, Apache or meta tag

How the checker reads a policy

To check a Content Security Policy, paste the header value, a Content-Security-Policy line or a meta tag, and this tool splits it into directives, applies the fallback rules of CSP Level 3 and lists every problem with a severity, a plain-words reason and a fix. It also accepts an nginx add_header line and an Apache Header line, and it takes the value out of them. Parsing follows the CSP Level 3 algorithm. The policy is split on semicolons, directive names are lower-cased, and when a name appears twice only the first one counts, so a stricter second copy is dropped and the tool reports it. Source lists are split on spaces and each entry is sorted into a keyword such as 'self', a nonce, a hash, a scheme such as https:, a host or a wildcard. Entries that match none of those are reported as invalid, because browsers skip them. If the text holds more than one policy (a comma between policies, or several header lines), the tool asks you to paste one at a time, because enforced and Report-Only policies are judged separately. A directive with non-ASCII characters is skipped whole, as CSP Level 3 says, and the tool reports it. The findings are sorted High, Medium, Low, Info. Info items are notes, not faults.

What each severity means and how the grade works

High means scripts can run that the policy meant to stop: 'unsafe-inline' in the list that governs scripts with no nonce, hash or 'strict-dynamic' next to it, or a wildcard, https:, http: or data: in that list. Medium covers 'unsafe-eval', no object-src (when default-src does not cover it either), no base-uri, no frame-ancestors, keywords written without quotes, unknown directives and invalid sources. Low covers inline styles, deprecated directives such as report-uri and block-all-mixed-content, and a missing form-action or default-src. Info only explains. No script directive for script tags, or none for event-handler attributes, is High as well. The grade is a rough guide, not an audit: start at 100, subtract 25 for each High, 10 for each Medium and 3 for each Low, and stop at 0. A is 90 or more, B 75, C 60, D 40, F below 40. Worked example: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; object-src self; form-action 'self'; report-uri /csp-report has one High ('unsafe-inline'), three Medium (object-src self needs quotes, no base-uri, no frame-ancestors) and one Low (report-uri without report-to). That is 100 - 25 - 30 - 3 = 42, grade D.

Fallback: which sources really apply

Many directives do not stand alone. CSP Level 3 gives each fetch directive a fallback list, most specific first. script-src-elem and script-src-attr use script-src, then default-src. style-src-elem and style-src-attr use style-src, then default-src. worker-src uses child-src, then script-src, then default-src. frame-src uses child-src, then default-src. The rest (connect-src, font-src, img-src, manifest-src, media-src, object-src) use default-src. Three directives never fall back: base-uri, form-action and frame-ancestors. When they are missing, nothing limits them. The effective policy table shows each row with the directive it comes from. Worked example: with default-src 'self'; script-src https://a.example, an image is limited to 'self' from default-src, a script tag to https://a.example from script-src, and a worker to https://a.example too, because with no worker-src and no child-src the list falls to script-src.

Nonces, hashes and strict-dynamic

A nonce is a random value in the header and in the nonce attribute of a script tag. CSP Level 3 says the server must make a new one on every response and that it should be at least 128 bits before encoding, which is 16 random bytes or 22 base64 characters. The tool flags shorter ones and values with characters that are not allowed. A hash source names sha256, sha384 or sha512; the digests are 32, 48 and 64 bytes, so the base64 text has a fixed length and a wrong length can never match. When a list holds a nonce or a hash, browsers ignore 'unsafe-inline' in it, so keeping it only helps very old browsers and is shown as Info. 'strict-dynamic' makes browsers ignore host sources, schemes, 'self' and 'unsafe-inline' for scripts, and trust scripts that a nonce'd script loads. With no nonce or hash next to it, no script can start. Section 8.5 of the draft describes a Strict CSP: nonces or hashes with 'strict-dynamic' in script-src, and base-uri set to 'self' or 'none'. The tool tells you when a policy has that shape.

Building a policy, and what this tool cannot tell you

Build mode starts from default-src 'self', script-src 'self', object-src 'none', base-uri 'self', form-action 'self' and frame-ancestors 'none', then adds what you tick: your own CDN hosts, the Google Analytics hosts Google lists for Tag Manager without Ads features, a nonce placeholder, images from https:, upgrade-insecure-requests and a report-to name. The result is run through the same checker, and the page shows the counts. It can be copied as a bare value, a header line, an nginx add_header line, an Apache Header line or a meta tag. A meta tag cannot hold frame-ancestors, report-uri or sandbox, and the tool leaves them out of that format. The nonce stays a placeholder: your server has to write a fresh value into the header and the page on each response, and a fixed value in a config file does not do that. The tool does not load your site, so it cannot know what your pages need and what a new policy would break. Start with Content-Security-Policy-Report-Only and read the reports. It also does not know which allowed hosts serve user-controlled scripts, so a clean grade is not proof of safety.

Frequently asked questions

How do I check my Content Security Policy?

Open your site's response headers in the browser developer tools, copy the Content-Security-Policy value or the whole line, and paste it here. Each finding shows a severity, the directive, the problem and a fix, such as adding object-src 'none' or base-uri 'self'. A meta tag works too, but then frame-ancestors, report-uri and sandbox are ignored by the browser.

Why is 'unsafe-inline' a High finding in script-src?

It lets every inline script and every onclick-style attribute run, including ones an attacker injects, which removes the main protection of a CSP. The fix is a nonce (a new random value on each response) or a sha256 hash for each inline script. Once the list holds a nonce or hash, browsers ignore 'unsafe-inline', and this tool lowers it to Info.

How do I stop clickjacking with a Content Security Policy?

Add frame-ancestors 'none', or frame-ancestors 'self' if your own pages embed each other. The directive does not fall back to default-src, so a policy without it lets any site embed your page in an iframe. It also does not work in a meta tag, so send it as an HTTP header.

How do I add Google Analytics to a CSP?

Google's Tag Manager guide lists, for Analytics without Ads features, https://www.googletagmanager.com for scripts, images and connections, https://*.google-analytics.com for images and connections, and https://*.google.com for connections. Tick the Google Analytics box in Build mode to add them. The Tag Manager snippet is an inline script, so it also needs the nonce box or its hash.

Can I use the same nonce on every page load?

No. CSP Level 3 says the server must generate a unique nonce each time it sends a policy, at least 128 bits long. A fixed nonce in nginx or Apache config can be copied by an attacker from any page. The placeholder in Build mode, nonce-REPLACE_WITH_RANDOM_VALUE, must be replaced by your application on every response.

Is my policy uploaded anywhere?

No. The text is processed in your browser and not uploaded. Your choice of mode and copy format is saved in this browser; the policy text and the build fields are not.