A Content Security Policy (CSP) is a response header that tells the browser which resources a page may load and run. It cannot replace input sanitizing, but it can block injected script if sanitizing fails. Roll it out in report-only mode first, then enforce a nonce-based strict policy.
What a CSP does
MDN describes CSP as a feature that helps prevent or minimize certain threats by restricting what a site's code may do. It is delivered in the Content-Security-Policy header as semicolon separated directives.
Content-Security-Policy: default-src 'self'; img-src 'self' example.com
Here default-src 'self' loads only same-origin resources by default, and images may also come from example.com.
How it helps against XSS
MDN calls CSP defense in depth. If an attacker gets script into your page, a CSP can still:
- block script tags that point to malicious sources
- disable inline script blocks, inline event handlers and javascript: URLs
- disable eval and similar string-to-code functions
MDN stresses that it does not replace sanitizing input. Do both.
Step 1: report only
Test a policy without enforcing it using the report-only header.
Content-Security-Policy-Report-Only: default-src 'self'; report-to csp-endpoint
Violations go to your reporting endpoint but nothing is blocked. MDN notes that a report-only policy cannot be delivered in a meta element. Read reports for a week or two and find the legitimate scripts you did not know about.
Step 2: a strict policy with nonces
MDN describes allowlists of hosts as hard to get right, because they often permit unsafe domains by accident. A strict CSP uses nonces or hashes.
Content-Security-Policy: script-src 'nonce-RANDOM'; object-src 'none'; base-uri 'none'
The server must create a new unpredictable nonce for every response, put it in the header and add the same nonce to each script tag it intends to run. Because the nonce changes each time, you need server-side templating. Static pages can use hashes instead, but those need recalculating whenever a script changes.
Add nonces only to your own script tags, so an attacker's injected scripts do not get one.
Step 3: enforce and keep watching
Switch the header from report-only to enforcing once reports are quiet. Keep the reporting endpoint, because a new third party script will show up as a violation. Treat that as a review prompt.
Common mistakes
- Using unsafe-inline to make errors go away, which defeats the purpose.
- Allowing wide host patterns.
- Never reading the reports.
Combine a CSP with careful cookie settings. See cookie flags explained.
Handling third party scripts
Analytics, chat widgets and tag managers are the usual reason a CSP fails. Take stock of each one.
- List every third party script and who approved it.
- Remove the ones nobody can explain.
- For the rest, load them through your nonce-bearing tag so they are covered by the policy, and check what further scripts they pull in.
- Watch the reports after each marketing change. A new tag will show up as a violation, which is the policy doing its job.
A tag manager that lets non-engineers add arbitrary script can undo your whole policy. Decide who may use it and review changes.
Quick check
Can a CSP stop all XSS?
No. It reduces the impact. Keep sanitizing and encoding output.
Will it break my site?
It can, which is why report-only mode comes first.
Sources
Last reviewed: 19 October 2026.