CSP Analyzer
Analyze the Content Security Policy header for any website. Parse all CSP directives, detect dangerous configurations like 'unsafe-inline' and 'unsafe-eval', identify missing critical directives, and get an actionable security score.
What is a Content Security Policy?
A Content Security Policy (CSP) is an HTTP response header that tells the browser exactly which resources it is allowed to load when rendering a page. Without a CSP, the browser will happily load scripts, styles, images, and fonts from any origin — including ones injected by an attacker. With a CSP, the browser enforces a strict allowlist and blocks anything that doesn't match.
CSP is the primary browser-level defense against cross-site scripting (XSS) attacks. While input sanitization and output encoding remain essential, CSP provides a critical second line of defense: even if an attacker manages to inject malicious content into your HTML, the browser will refuse to execute it if it violates the policy.
Who needs a CSP? Any website that serves HTML pages to users. Even static sites benefit from a CSP that blocks inline script execution. High-value targets — banking, healthcare, SaaS, government — should treat CSP as non-negotiable. A weak or absent CSP is one of the most common findings in web application security assessments.
How CSP directives work
A CSP header is a semicolon-separated list of directives. Each directive names a resource type and then lists the origins or keywords allowed for that type.
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
| Directive | Controls | Falls Back to default-src? |
|---|---|---|
default-src |
Fallback for all fetch directives not explicitly set | N/A (it is the fallback) |
script-src |
JavaScript execution — the most important directive for XSS | Yes |
style-src |
CSS stylesheets and inline style attributes | Yes |
img-src |
Image sources including <img> and CSS background-image | Yes |
object-src |
Browser plugins: <object>, <embed>, <applet> | Yes |
connect-src |
Ajax, fetch(), WebSocket, and EventSource connections | Yes |
frame-ancestors |
Which origins may embed this page in an iframe | No — must be set explicitly |
base-uri |
Allowed values for the <base> element's href | No — must be set explicitly |
form-action |
Valid targets for form submission | No — must be set explicitly |
Common mistake: Many developers set default-src 'self'
and assume it covers frame-ancestors,
base-uri, and
form-action. It does not.
These three directives must always be set explicitly — they do not fall back to
default-src.
The most dangerous CSP misconfigurations
Not all CSP headers provide meaningful protection. Some configurations are technically valid but leave significant attack surface open. Here are the patterns this tool detects and why they matter.
'unsafe-inline' in script-src
Allows all inline <script> blocks,
inline event handlers (onclick, etc.),
and javascript: URIs.
XSS attacks typically inject inline scripts — this keyword removes the protection
that would otherwise block them. Replace with nonces or hashes.
Wildcard (*) in script-src
Allows scripts to be loaded from any origin. A CSP with
script-src *
provides no XSS protection — an attacker can load a script from any domain they
control. Use an explicit allowlist of trusted origins instead.
'unsafe-eval' in script-src
Permits eval(),
new Function(),
and string-form setTimeout.
These functions turn strings into executable code, creating an attack path for
injected content. Most production applications can be refactored to remove eval usage.
Missing frame-ancestors
Without frame-ancestors,
any website can embed your page in an invisible iframe and use it for clickjacking —
tricking users into clicking buttons they can't see. Set
frame-ancestors 'none'
unless embedding is intentional.
Missing base-uri
Without base-uri,
an attacker who can inject a
<base href="https://evil.com">
tag can cause all relative URLs on the page — scripts, links, form actions — to
resolve against their domain. This bypasses even a strong script-src.
http: scheme in script-src
Allowing http:
in script-src means scripts can be loaded over unencrypted connections. An attacker
in a position to intercept HTTP traffic (coffee shop Wi-Fi, ISP, corporate proxy) can
modify the script in transit. Require HTTPS for all script sources.
How to build a strong Content Security Policy
A strong CSP is one that provides meaningful XSS protection without breaking the application's functionality. The recommended approach is to start strict and progressively open up what the application actually needs.
Strict baseline (nonce-based)
Content-Security-Policy:
default-src 'self';
script-src 'nonce-{random}' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
form-action 'self';
upgrade-insecure-requests
Generate a fresh nonce per request and add it to every <script> tag.
Deployment approach
- Start with
Content-Security-Policy-Report-Onlyand a report endpoint to collect violations without breaking anything. - Analyze violation reports for a week or two to identify legitimate sources being blocked.
- Add those sources to the policy explicitly.
- Switch to the enforcing
Content-Security-Policyheader when violations stop. - Keep
Report-Onlyrunning alongside enforcement to catch future issues.
The nonce + strict-dynamic pattern is the W3C-recommended approach for
production applications. Each page render generates a cryptographically random nonce
(e.g. 32 random bytes, base64-encoded). The nonce goes in the CSP header and in every
<script nonce="...">
tag. 'strict-dynamic'
then allows scripts loaded by nonce-bearing scripts to also load, solving the problem
of dynamically-injected third-party scripts.
Frequently asked questions
What is a Content Security Policy (CSP)?
A Content Security Policy is an HTTP response header that tells the browser which resources it is allowed to load for a given page. By restricting which origins can serve scripts, styles, images, and fonts, CSP is the primary browser-enforced defense against cross-site scripting (XSS) attacks. Even if an attacker injects malicious content into your HTML, the browser will refuse to execute it if it violates the policy. CSP is defined by the W3C CSP specification and supported in all modern browsers.
What does 'unsafe-inline' mean and why is it dangerous?
'unsafe-inline' in script-src allows all inline <script> blocks, inline event handlers (onclick, onload, etc.), and javascript: URIs to execute without restriction. XSS attacks typically inject inline scripts — and 'unsafe-inline' removes the CSP protection that would otherwise block them. It is the single most impactful CSP weakness. Replace 'unsafe-inline' with nonces (unique tokens per request) or cryptographic hashes to allow specific inline scripts while blocking injected ones.
What does 'unsafe-eval' mean in a CSP?
'unsafe-eval' in script-src permits JavaScript's dynamic code execution functions: eval(), new Function(), setTimeout(string), and setInterval(string). These evaluate arbitrary strings as executable code at runtime. If an attacker can control the string passed to eval(), they can execute arbitrary JavaScript. Most production applications — including complex SPAs — can be configured to avoid eval: Webpack has production settings that eliminate eval, and most frameworks support an eval-free build.
What is 'strict-dynamic' and how does it help?
'strict-dynamic' delegates trust transitively: when combined with a nonce, it trusts not just the nonce-bearing script but also scripts that trusted script loads dynamically. This solves the biggest practical problem with strict CSPs — that dynamically-injected third-party scripts (analytics, chat widgets, payment iframes) would be blocked. 'strict-dynamic' also causes modern browsers to ignore 'unsafe-inline' and allow-listed hosts in the same directive, making it the recommended approach for CSP Level 3. Combine it with a nonce for the strongest posture.
Does default-src cover frame-ancestors and base-uri?
No. This is a very common CSP mistake. default-src is a fallback for all fetch directives (script-src, style-src, img-src, connect-src, etc.) but it does NOT cover three directives: frame-ancestors, base-uri, and form-action. These must always be explicitly set. Without an explicit frame-ancestors, any origin can embed your page in an iframe. Without base-uri, any injected <base> tag can hijack all relative URLs on the page. Set all three explicitly in every CSP.
Should I use nonces or hashes?
Both nonces and hashes allow specific inline scripts without 'unsafe-inline'. Nonces are cryptographically random values generated per-request: the server adds the nonce to the CSP header and each <script nonce="..."> tag; the browser only executes scripts whose nonce matches. Hashes are computed over the exact script content; the browser verifies the content matches the hash. Nonces are better for dynamic pages; hashes work well for static content. For most applications, nonces combined with 'strict-dynamic' is the most practical approach.
What is frame-ancestors and why should I set it?
frame-ancestors controls which origins can embed your page in an iframe, frame, or object element. Without it, any website can embed your page invisibly and use it for clickjacking attacks — tricking users into clicking actions they can't see. Set frame-ancestors 'none' if your page is never meant to be embedded, or frame-ancestors 'self' for same-origin embedding only. frame-ancestors supersedes the older X-Frame-Options header in browsers that support CSP Level 2, and does not fall back to default-src.
How do I test a CSP before enabling enforcement?
Use the Content-Security-Policy-Report-Only header. This header tells the browser to evaluate the policy and log any violations but NOT enforce them — pages load normally. Set a report-to directive pointing at a violation reporting endpoint to collect the data. After a week or two of running in report-only mode, review the violations to identify legitimate resources being blocked and add them to the policy. Once violations stop, switch to the enforcing Content-Security-Policy header. Keep report-only running alongside enforcement to catch regressions from future code changes.