Content Security Policy: A Practical Guide
A Content Security Policy (CSP) is an HTTP header that tells the browser exactly which scripts, styles, and other resources a page may load or run. Its headline job is defense against cross-site scripting: even if an attacker injects markup, a strict CSP decides whether that markup can actually execute. It is the second layer, after output encoding, that assumes the first will someday fail.
How does CSP blunt XSS?#
Output encoding tries to stop injection; CSP limits the damage when something slips through. A
policy that only permits scripts carrying a server-generated nonce means an injected
<script> — which cannot know the nonce — simply will not run:
Content-Security-Policy:
script-src 'nonce-r4Nd0m' 'strict-dynamic';
object-src 'none';
base-uri 'none'
'strict-dynamic' lets your nonce-marked scripts load their own dependencies, which is what
makes this deployable on real apps. object-src 'none' and base-uri 'none' close common
bypass avenues.
Nonces vs allowlists#
Older CSPs listed allowed hostnames, which proved fragile:
| Approach | Problem |
|---|---|
| Host allowlist | Bypassable via trusted CDNs, JSONP, open redirects |
Nonce + strict-dynamic | Attacker cannot guess the per-response nonce |
The nonce approach — a fresh, unpredictable nonce per response — is dramatically more robust and easier to maintain.
How do you roll out CSP without breaking the site?#
Deploy in report-only mode first:
- Send
Content-Security-Policy-Report-Onlywith your intended policy. - Collect violation reports from real traffic.
- Fix legitimate scripts the policy would block (add nonces, remove inline handlers).
- When reports go quiet, switch to the enforcing header.
CSP is the most valuable of the HTTP security headers. More at the Web Security hub.
Frequently asked questions#
What does a Content Security Policy do?
A CSP is an HTTP response header that tells the browser which sources of scripts, styles, images, and other resources a page is allowed to load or execute. Its main security value is limiting what an injected script can do, turning many cross-site scripting bugs from exploitable into blocked and reportable.
Why are nonce-based policies better than allowlists?
Host allowlists are hard to get right and often bypassable through trusted-but-abusable domains or JSONP endpoints. A nonce-based policy with strict-dynamic marks each legitimate script with a fresh random value the attacker cannot guess, so injected markup cannot execute — a far more robust design that Google’s research recommends.