Free Tool
Content Security Policy (CSP) Checker
Enter any URL to instantly validate its Content-Security-Policy header. See which directives are missing, which values are dangerously permissive, and how to fix them — in seconds, no account needed.
Sign in with Google or GitHub to run the scan. Your first scan is free — no credit card required.
ZeriFlow Data — 12,400+ sites analyzed
Only 36% of sites in our corpus have a Content-Security-Policy header. Of those that do, 71% include unsafe-inline in script-src — which completely negates XSS protection.
What is a Content Security Policy?
CSP is an HTTP response header that tells browsers which sources are allowed to load content — scripts, styles, images, fonts, iframes. It is the most effective client-side protection against XSS attacks. Without CSP, any JavaScript injected into your page by an attacker runs with full permissions in your visitors' browsers.
CSP works through directives. Each directive controls a specific content type:
default-srcFallback for all content types not explicitly defined
script-srcControls JavaScript sources
style-srcControls CSS sources
img-srcControls image sources
connect-srcControls fetch/XHR/WebSocket connections
frame-ancestorsControls who can embed your page (replaces X-Frame-Options)
object-srcControls Flash/plugins — always set to 'none'
base-uriPrevents base tag injection attacks
The Biggest CSP Mistakes
1. unsafe-inline in script-src
This keyword allows all inline scripts to execute — including any script an attacker injects via an XSS vulnerability. It completely negates XSS protection. Use nonces instead.
# BAD — negates XSS protection Content-Security-Policy: script-src 'self' 'unsafe-inline' # BETTER — use nonces Content-Security-Policy: script-src 'self' 'nonce-r4nd0m'
2. unsafe-eval in script-src
Allows eval(), setTimeout(string), and new Function() — all XSS vectors. Never use this unless you have no alternative and have exhausted all other options.
3. Wildcard * in default-src
Allows loading from any origin. This is equivalent to having no CSP at all — an attacker can inject a script tag pointing to their server and the browser will execute it.
4. Missing object-src 'none'
Flash plugins — even if considered dead — can bypass script-src restrictions without this directive explicitly set to 'none'. Always include it.
5. No frame-ancestors
Without this directive, your site can still be iframed by attackers for clickjacking attacks. Set frame-ancestors 'none' if you never need to be embedded, or specify trusted origins explicitly.
How to Implement CSP Without Breaking Your Site
Start with report-only mode. Deploy the header below and collect violations for 1-2 weeks before enforcing anything:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
Recommended progression once you switch to enforcement mode:
- 01
Start with default-src 'self'
- 02
Add nonces for inline scripts — remove unsafe-inline
- 03
Restrict specific third-party domains (analytics, fonts, CDNs)
- 04
Add frame-ancestors 'none' or a trusted origin allowlist
- 05
Add object-src 'none' to block plugin execution
- 06
Enable upgrade-insecure-requests to force HTTPS for subresources
CSP Examples by Framework
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$request_id'; object-src 'none'; frame-ancestors 'none'" always;
Next.js (next.config.js)
// next.config.js
headers() {
return [{
source: '/(.*)',
headers: [{
key: 'Content-Security-Policy',
value:
"default-src 'self'; " +
"script-src 'self' 'nonce-{n}'; " +
"object-src 'none'; " +
"frame-ancestors 'none'"
}]
}]
}Express (helmet)
import helmet from 'helmet';
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
}
}));Frequently Asked Questions
What does a Content-Security-Policy header do?
A Content-Security-Policy header tells the browser which sources are allowed to load scripts, styles, images, fonts, and other resources on your page. If a resource is not explicitly permitted, the browser refuses to load it. This makes it the most effective client-side defense against Cross-Site Scripting (XSS) attacks — an injected script from an unauthorized origin is simply blocked before it executes.
Why does my site break when I add CSP?
CSP breaks sites when inline scripts or styles are present without a nonce or hash, when external resources like Google Fonts, analytics scripts, or CDN-hosted libraries are not listed in the relevant directives, or when eval() is used by a JavaScript framework. The safest approach is to start with Content-Security-Policy-Report-Only mode, collect violation reports for 1-2 weeks, then tighten the policy incrementally.
What is the difference between CSP and CSP-Report-Only?
Content-Security-Policy actively blocks policy violations — the browser refuses to load non-compliant resources. Content-Security-Policy-Report-Only does nothing to the page but sends a violation report to the URI specified in the report-uri directive. Report-Only is the standard way to test a new policy in production without risking breakage.
Should I use 'unsafe-inline' in my CSP?
No. 'unsafe-inline' in script-src completely defeats XSS protection — it allows any inline script on the page to execute, including injected ones. The correct solution is nonce-based CSP: generate a cryptographically random nonce per request, add it to your script tags as a nonce attribute, and include 'nonce-{value}' in your script-src directive. Frameworks like Next.js support nonce injection via middleware.
How do I add a CSP nonce in Next.js?
In Next.js 13+, create a middleware.ts file at the project root. Generate a nonce with crypto.randomUUID() or the Web Crypto API, set it in a response header (e.g. x-nonce), then read it in your root layout to pass to the next.config.js headers() function and to your Script components via the nonce prop. The Next.js documentation has a complete example for App Router projects.