Content Security Policy: writing one that actually protects
A Content-Security-Policy tells the browser which scripts it may run. Done well, an injected <script> simply does not execute. Done loosely, the header is there and protects nothing.
Example
Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval'Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; upgrade-insecure-requestsWhat weakens a CSP
'unsafe-inline' in script-src allows exactly the inline scripts and event handlers an injection produces. 'unsafe-eval' lets strings become code. Sources such as *, https: or data: allow script from any host. A policy with no script-src or default-src does not restrict scripts at all, and a report-only policy blocks nothing. Missing object-src 'none' and base-uri leave two older bypasses open.
How to build one
Start strict in report-only mode, fix what it reports, then enforce.
- Use a per-response nonce on your scripts, plus 'strict-dynamic' for the scripts they load
- Add object-src 'none'; base-uri 'self'; frame-ancestors 'self'
- Move inline event handlers into script files
- Send it as a header — frame-ancestors does not work in a meta tag
How RepoVerse finds it
- ModerateNo Content-Security-Policy
If any page has an injection bug, the injected script runs with nothing to stop it — CSP is the layer that limits what one XSS can do.
website · CWE-693
- LowContent-Security-Policy is only reported, not enforced
A report-only policy logs violations but blocks nothing, so it gives no protection yet.
website · CWE-693
- ModerateCSP allows inline scripts
`'unsafe-inline'` in `script-src` lets injected `<script>` tags and event handlers run, which is the main thing a CSP is there to stop.
website · CWE-79
- LowCSP allows eval
`'unsafe-eval'` lets strings become code through `eval`, `new Function` and string timers, which turns some injection bugs into script execution.
website · CWE-95
- ModerateCSP lets scripts load from anywhere
A `script-src` of `*`, `https:`, `http:` or `data:` allows script from any host, so an attacker who can inject a tag can load their own file and the policy will allow it.
website · CWE-693
- LowCSP leaves plugins or the base URL open
Without `object-src 'none'` old plugin content can still execute; without `base-uri` an injected `<base>` tag can redirect every relative script URL on the page.
website · CWE-693
Questions
- Does a nonce make 'unsafe-inline' safe?
- When a nonce or hash is present, CSP Level 2 browsers ignore 'unsafe-inline', so keeping it only helps very old browsers. RepoVerse does not report 'unsafe-inline' when a nonce or hash is in the policy.
- Can I use CSP with Next.js or React?
- Yes. Next.js supports nonces through middleware, and React renders without inline scripts by default. Third-party tags are usually what make a strict policy hard.
Related
- Cross-site scripting (XSS): how it works and how to stop itReflected, stored and DOM-based XSS (CWE-79) explained with vulnerable and fixed code, plus the template, CSP and cookie settings that limit the damage.
- Clickjacking: stolen clicks through an invisible frameHow clickjacking (CWE-1021) hides your page in an invisible frame to steal clicks, and the two headers — CSP frame-ancestors and X-Frame-Options — that prevent it.
- Security headers checker with a fix for every gapCheck a site's HTTP security headers — Content-Security-Policy, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy — and get the exact line to add for each gap.
Updated 2026-10-11