DevKitHub

API & Security

Content Security Policy Analyzer

Paste a Content-Security-Policy value to have each directive explained and the gaps named. The checks are the ones a reviewer would make by hand.

1 line
1 high · 4 total2 directives

Directives

default-src'self'
The fallback for every fetch directive that is not set explicitly.
script-src'self' 'unsafe-inline'
Where scripts may be loaded from, and whether inline script may run.
  • high

    'unsafe-inline' allows any inline <script> to run, which is the exact thing an injected script is. A policy with this in script-src stops very little.

    Fix: Move inline scripts to files, or authorise each one with a per-response nonce or a hash.

  • medium

    base-uri has no fallback to default-src. Unset, an injected <base> tag can repoint every relative URL on the page, including script sources, without violating the rest of the policy.

    Fix: Set base-uri 'self' or 'none'.

  • medium

    frame-ancestors is unset, so the page can be framed by anyone. This directive does not fall back to default-src, and it is the modern replacement for X-Frame-Options.

    Fix: Set frame-ancestors 'none' if the page should never be framed, or list the origins that may.

  • low

    form-action is unset and does not fall back to default-src, so an injected form can post anywhere.

    Fix: Set form-action 'self'.

This tool runs entirely in your browser. Your input is never uploaded, stored or logged.

How it works

CSP is the header most often shipped in a state that looks protective and is not. A policy with unsafe-inline in script-src blocks almost nothing an attacker who can inject markup would care about, and one with a wildcard host is barely narrower — but both look substantial in a header dump, which is exactly the problem. Each directive here is explained in a sentence, and then the policy is checked for the specific ways it can be hollow.

The most valuable checks are about directives that are not there. base-uri, frame-ancestors and form-action do not fall back to default-src, so a policy that sets only default-src looks complete while leaving all three unrestricted. An injected base tag can repoint every relative URL on the page including script sources; an unset frame-ancestors means anyone can frame you. Those three absences are reported as findings rather than left to be noticed.

Context is applied where it changes the answer. unsafe-inline alongside a nonce is reported as low rather than high, because a browser that understands nonces ignores unsafe-inline entirely — it is a deliberate fallback for old browsers, not a hole in current ones. strict-dynamic without a nonce or hash is reported as high, because it discards every host source and then has nothing to bootstrap trust from. There is no score out of a hundred: a policy is fit for a particular page or it is not, and a number invites tuning the number.

Common problems

Every example below is run against this tool in our test suite, so what it says here is what the tool actually does.

The policy looks strict but injected script still runs.

Why:
script-src contains 'unsafe-inline', which permits any inline script element to execute — which is exactly what an injected script is.
Fix:
Move inline scripts into files, or authorise each one with a per-response nonce or a hash.

default-src is set and the page is still frameable.

Why:
frame-ancestors does not fall back to default-src. Nor do base-uri or form-action. A policy with only default-src leaves all three completely unrestricted.
Fix:
Set frame-ancestors, base-uri and form-action explicitly. 'none' or 'self' is right for most pages.

A directive was added and nothing changed.

Why:
It is misspelled, or it is not a directive browsers implement. Unknown directives are ignored silently, so a typo leaves whatever it was meant to restrict entirely unrestricted.
Fix:
Check the name against the analyzer output — unknown directives are flagged.

Frequently asked questions

Is 'unsafe-inline' always wrong?
In script-src it defeats most of the point of having a policy, because an injected inline script is precisely what it permits. Alongside a nonce it is harmless on current browsers, which ignore it entirely when a nonce is present — that combination is reported as a low-severity legacy fallback rather than a hole.
Why does it complain about base-uri when default-src is set?
Because base-uri does not fall back to default-src. Neither does frame-ancestors or form-action. This is the single most common way a policy that reads as complete leaves real gaps.
Why is there no score?
Because a policy is fit for a particular page or it is not, and a number invites tuning the number rather than fixing the gap. Every finding says what is wrong and what to do instead.

Read more about this

Last updated