WebPeeks
W3C CSP Level 3

Content-Security-Policy evaluator

Paste a Content-Security-Policy and read it the way a browser does: which directive wins, what falls back to what, and which sources quietly undo the protection.

Paste-in tool. Your browser cannot fetch another site's headers for a web page (CORS forbids it), and WebPeeks has no server to do it, so nothing you paste leaves your browser. Paste the policy itself, the whole header line, or the page's <meta http-equiv="Content-Security-Policy"> tag. On Windows use curl.exe -sI https://example.com/ | findstr /i content-security.

curl -sI https://example.com/ | grep -i content-security-policy
Directives separated by semicolons. Line breaks are fine.

1 to fix · 2 warnings · 2 passed · 3 notes

  • Fail
    script-src: 'unsafe-inline' allows inline script

    Any injected <script> block or onclick= attribute runs, which is exactly what CSP exists to prevent. Replace it with nonces or hashes.

  • Warn
    object-src is 'self' (from default-src)

    Recommended value is 'none'. It costs nothing on a modern site.

  • Warn
    No base-uri

    base-uri does not fall back to default-src. Without it, an injected <base href> tag can redirect every relative script URL on the page to an attacker's host, bypassing nonce-based policies. Set base-uri 'none' or 'self'.

  • Info
    script-src: host allowlist (2 hosts)

    Allowlists are often bypassable: if any listed host serves JSONP endpoints, user uploads or an old copy of AngularJS, an attacker can run script from it. Nonces with 'strict-dynamic' avoid the problem.

  • Info
    No form-action

    form-action does not fall back to default-src, so an injected form could post to any host. form-action 'self' is a cheap addition if all forms submit to your own site.

  • Info
    style-src allows 'unsafe-inline'

    Very common and much lower risk than inline script, but injected CSS can still exfiltrate some data through attribute selectors. Worth removing when practical.

  • Pass
    frame-ancestors 'none'

    Controls who may frame the page; this takes precedence over X-Frame-Options.

  • Pass
    Violation reporting is configured

    Reports show what the policy blocks in real traffic, which is how to tighten it safely.

Directives as written

Parsed directives
DirectiveSources
default-src'self'
script-src'self' 'unsafe-inline' https://cdn.example.net https://analytics.example.org
style-src'self' 'unsafe-inline'
img-src* data:
connect-src'self' https://api.example.com
frame-ancestors'none'
report-uri/csp-report

What each resource type actually gets

Effective directives after fallback
ResourceAllowed sourcesWhere it comes from
script-src'self' 'unsafe-inline' https://cdn.example.net https://analytics.example.orgset directly
script-src-elem'self' 'unsafe-inline' https://cdn.example.net https://analytics.example.orgfrom script-src
script-src-attr'self' 'unsafe-inline' https://cdn.example.net https://analytics.example.orgfrom script-src
style-src'self' 'unsafe-inline'set directly
img-src* data:set directly
font-src'self'from default-src
connect-src'self' https://api.example.comset directly
media-src'self'from default-src
object-src'self'from default-src
frame-src'self'from default-src
worker-src'self' 'unsafe-inline' https://cdn.example.net https://analytics.example.orgfrom script-src
manifest-src'self'from default-src
base-urinot restricted—
form-actionnot restricted—
frame-ancestors'none'set directly

How a browser reads a policy

The rules come from the W3C's Content Security Policy Level 3 specification. A policy is a list of directives separated by semicolons. Each directive is a name followed by a list of sources, separated by spaces. Three rules catch people out:

The second table in the result applies these rules, so you can see what each resource type is actually allowed, rather than what the policy appears to say.

What is checked, and why

Most of the checks concern scripts, because stopping injected script (cross-site scripting) is the main job of a CSP. The approach follows the research published by Google's security team (Weichselbaum and others, CSP Is Dead, Long Live CSP!, ACM CCS 2016), which found that most real-world policies based on host allowlists could be bypassed, and recommended nonces or hashes with 'strict-dynamic' instead.

PatternVerdictWhy
'unsafe-inline'Fail (script)Any injected <script> or onclick runs. Ignored if a nonce or hash is also present.
* , https: , data:Fail (script)Script can come from anywhere an attacker can host a file.
'unsafe-eval'WarnAllows eval() and new Function(), widening what an injection can do.
object-src not 'none'WarnPlugin content is obsolete; 'none' costs nothing.
no base-uriWarnAn injected <base> tag can repoint relative script URLs, even under a nonce policy.
host allowlistInfoWorks, but is bypassable if any listed host serves JSONP or user uploads.
nonce + 'strict-dynamic'PassThe recommended design; allowlists are ignored by browsers that support it.

A worked example

The weak example above is the kind of policy many sites ship first. It has a default-src 'self', which looks restrictive, but then:

Press Strict example to compare a policy built the recommended way: a per-response nonce, 'strict-dynamic', object-src 'none' and base-uri 'none'. Its https: and 'unsafe-inline' are there only for very old browsers and are reported as ignored rather than as failures.

Moving from an allowlist to nonces

  1. Generate a fresh random nonce (at least 128 bits, base64) for every response, in the server or framework, never a fixed value.
  2. Add nonce="…" to every <script> tag the page itself writes, including the loader snippets for analytics or tag managers.
  3. Move inline event handlers (onclick="…") into script files; nonces do not cover them.
  4. Ship script-src 'nonce-…' 'strict-dynamic'; object-src 'none'; base-uri 'none' as Report-Only first, read the reports, then enforce it.

Limits of a static check

This evaluator reads text. It cannot know whether a host you allow serves a JSONP endpoint, whether your nonce is really random per response, or whether a script you trust with 'strict-dynamic' will load something unsafe. It evaluates the first policy only, where a header carries several separated by commas (browsers enforce all of them together). And it does not know which browsers your visitors use: directives such as require-trusted-types-for are enforced in Chromium-based browsers only.

Common questions

What is the difference between a nonce and a hash?

A nonce is a random value the server generates for every response and puts both in the CSP header and on each <script nonce="..."> tag; an attacker cannot guess it, so injected scripts lack it. A hash is the base64 SHA-256 (or 384, 512) of a specific inline script's exact text; it suits static pages where the inline script never changes. Nonces need a server that renders each page; hashes work on static hosting.

Why does my policy list https: and 'unsafe-inline' next to a nonce?

That is the backward-compatible form of a strict policy. Browsers that understand 'strict-dynamic' ignore https: and 'unsafe-inline' once a nonce or hash is present, so modern browsers get the strict behaviour, while very old browsers that do not understand nonces fall back to something that at least does not break the site.

Can I test a policy without breaking my site?

Yes. Send it as Content-Security-Policy-Report-Only with a report-uri or report-to endpoint. Browsers apply nothing, but report every resource the policy would have blocked. Watch the reports for a week or two, adjust, then switch the header name to Content-Security-Policy.

Does default-src cover base-uri, form-action and frame-ancestors?

No. default-src is the fallback only for fetch directives such as script-src, img-src and connect-src. base-uri, form-action and frame-ancestors have no fallback, so a policy that sets only default-src 'self' leaves all three unrestricted. The "What each resource type actually gets" table above shows this for any policy you paste.

Is style-src 'unsafe-inline' a problem?

Much less than script. Injected CSS cannot run code, but it can change what a page shows and, with attribute selectors that load background images, leak some values from the page to another server. Many sites keep 'unsafe-inline' for styles because frameworks and libraries set inline styles; it is reasonable to fix script first.