Content Security Policy Explained: Add a CSP Without Breaking Your Site
3 min read4 October 2026By Muhammad Abdullah
A Content Security Policy is a response header that tells the browser which sources a page is allowed to load scripts, styles, images and other resources from. Its main job is to limit the damage of cross-site scripting: even if an attacker manages to inject a script into your page, the browser refuses to run it. It is powerful, and it is easy to break a site with, so it is worth rolling out carefully.
What a policy looks like
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com;
img-src 'self' data: https:;
style-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none'It is sent as a single header line; it is split here only to read. Each directive covers one kind of resource and lists where it may come from. 'self' means your own origin.
The directives that matter most
default-src: the fallback for any resource type you do not list separately.script-src: where JavaScript may load from. The most important line for security.style-src,img-src,font-src: stylesheets, images and fonts.connect-src: where the page may send requests with fetch, XHR and WebSockets.frame-ancestors: which sites may show your page inside a frame. Setting it to'none'stops clickjacking and replaces the older X-Frame-Options header.
unsafe-inline undoes most of it
An injected attack is almost always an inline script, written straight into the page. Allowing 'unsafe-inline' in script-src tells the browser to run inline scripts, which is exactly what the attacker needs. The policy still exists, but it no longer protects against the main attack it was made for.
The alternatives let you keep the inline scripts you wrote while blocking any that were injected:
- A nonce: a random value generated fresh for every response, added to the header and to each of your own script tags. An injected script will not know it.
- A hash: the SHA-256 hash of a specific inline script's content, listed in the policy. Only that exact script runs.
Content-Security-Policy: script-src 'self' 'nonce-r4nd0mV4lue'
<script nonce="r4nd0mV4lue">/* your inline script */</script>Roll it out in report-only mode first
Send the policy as Content-Security-Policy-Report-Only first. The browser enforces nothing, but reports every resource that would have been blocked, in the console and to a reporting address if you set one. Browse the whole site, collect the reports, add the sources you really use, and only then switch to the enforcing header.
Header or meta tag
A CSP can also be set with a meta tag in the page, but some directives, including frame-ancestors and reporting, only work in the header. Use the header where you can.
Common questions
What does a Content Security Policy protect against?
Mainly cross-site scripting, by stopping injected scripts from running, and clickjacking through the frame-ancestors directive.
Is unsafe-inline bad?
In script-src, yes: it allows injected inline scripts to run, which removes most of the protection. Use nonces or hashes instead.
How do I test a CSP without breaking my site?
Send it as Content-Security-Policy-Report-Only, review what would be blocked, adjust, then switch to the enforcing header.