Content-Security-Policy explained (with a safe rollout)
CSP is the strongest defence against XSS — and the easiest header to get wrong. Here's how it works, a safe starting policy, and how to reach a strict nonce-based CSP.
Content-Security-Policy (CSP) tells the browser exactly which sources of scripts, styles, images and other resources are allowed to load — so even if an attacker injects a <script>, the browser refuses to run it. It's the most effective mitigation for cross-site scripting (XSS), and the header most likely to block your own site if you rush it.
The key directives#
| Directive | Controls |
|---|---|
| default-src | Fallback for anything not otherwise specified |
| script-src | Where scripts may load/run (the XSS-critical one) |
| style-src | Stylesheets and inline styles |
| img-src | Images (allow https: for remote images) |
| connect-src | fetch/XHR/WebSocket destinations |
| frame-ancestors | Who may iframe you (replaces X-Frame-Options) |
| object-src / base-uri | Set both to 'none' / 'self' to close injection vectors |
A safe starting policy#
Deploy in report-only first — it reports violations without blocking anything:
Content-Security-Policy-Report-Only:
default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
img-src 'self' data: https:;
script-src 'self';
style-src 'self' 'unsafe-inline';
connect-src 'self'Watch the reports for a week, add the legitimate sources it flags, then switch the header name to Content-Security-Policy to enforce.
Reaching a strict CSP#
Allowing 'unsafe-inline' in script-src weakens the whole policy. The modern, strict approach is a per-request nonce plus 'strict-dynamic':
script-src 'nonce-RANDOM' 'strict-dynamic';- Your server generates a fresh random nonce per response and stamps it on every trusted
<script nonce="…">. 'strict-dynamic'lets those trusted scripts load others, so you don't have to allowlist every CDN.- Trade-off: a per-request nonce means pages can't be statically cached with a fixed nonce — weigh it against your performance needs.
Frequently asked questions
▸ ▾ How do I add CSP without breaking my site?
Deploy it as Content-Security-Policy-Report-Only first. That reports violations to you but blocks nothing. Fix the legitimate sources it flags, then rename the header to Content-Security-Policy to start enforcing.
▸ ▾ Is 'unsafe-inline' bad?
It defeats much of CSP's XSS protection because it allows any inline script to run. It's a pragmatic starting point, but the strict goal is a nonce (or hash) based script-src with 'strict-dynamic' and no 'unsafe-inline'.
▸ ▾ Does CSP replace X-Frame-Options?
The frame-ancestors directive supersedes X-Frame-Options in modern browsers. Sending both is fine for older-browser coverage.
Do it with a free tool
Related guides
Published 2026-07-09 · Updated 2026-07-09 · By Crawlsonar.