Content Security Policy (CSP) Explained
Part of our guide to HTTP Security Headers Explained: The Complete Guide.
A Content Security Policy tells the browser exactly which sources a page is permitted to load resources from, and it refuses everything else. That single rule shuts down the bulk of cross-site scripting attacks, because injected code from an unapproved origin simply never executes. CSP is delivered as the Content-Security-Policy response header, and it sits alongside the other defenses covered in HTTP security headers explained.
The mechanism is a whitelist, not a scanner. Rather than trying to guess which scripts are malicious, CSP flips the model: you declare the handful of origins you trust, and the browser blocks the rest by default. The W3C CSP Level 3 specification defines how each directive is parsed and enforced, and the OWASP Secure Headers project tracks sensible baseline values you can copy.
How a policy is built
A policy is a list of directives separated by semicolons. Each directive names a resource type and the sources allowed for it. The keyword 'self' means the page's own origin; a domain like https://cdn.example.com allows that host; 'none' blocks everything.
A minimal policy might read: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'. That allows scripts and other resources only from your own origin, forbids plugins, and stops other sites from framing the page.
Key directives
| Directive | Controls | Common safe value |
|---|---|---|
default-src |
Fallback for any fetch directive not set explicitly | 'self' |
script-src |
JavaScript sources | 'self' plus nonces or hashes |
style-src |
Stylesheet sources | 'self' |
img-src |
Image sources | 'self' data: |
connect-src |
Targets for fetch, XHR, WebSocket | 'self' plus your API host |
frame-ancestors |
Who may frame this page (clickjacking) | 'none' or 'self' |
object-src |
Plugins such as Flash and applets | 'none' |
default-src is the backstop. If you do not set script-src, scripts fall back to whatever default-src allows, so always pin the sensitive ones explicitly. frame-ancestors deserves special mention because it replaces the older X-Frame-Options header and handles clickjacking more flexibly.
Allowing inline scripts the right way
The hard part of CSP is inline code. Many sites have script tags written directly into the HTML, and a strict policy blocks them. There are two clean ways to permit specific inline scripts without weakening the policy.
A nonce is a random token generated fresh on every request. You add it to the header (script-src 'self' 'nonce-r4nd0m') and to the matching script tag. Because the value changes each load, an attacker cannot predict it, so injected inline scripts still fail.
A hash works for static inline scripts that never change. You compute a SHA-256 hash of the script contents and list it (script-src 'sha256-...'). The browser runs the inline block only if its hash matches.
Report-Only and safe rollout
Switching on a strict policy blind will almost certainly break something. Use Content-Security-Policy-Report-Only first. It applies the policy, logs every violation to a reporting endpoint, but blocks nothing. Watch the reports for a week or two, fix the legitimate sources you forgot, then promote the same policy to the enforcing header.
Pitfalls that defeat the purpose
Two values quietly cancel most of CSP's protection. Adding 'unsafe-inline' to script-src re-enables all inline scripts, which is exactly the attack vector CSP exists to close. Adding 'unsafe-eval' permits eval() and similar dynamic execution. If you find yourself reaching for either, prefer nonces or hashes instead, because a policy with 'unsafe-inline' offers little more than decoration.
Other common traps: forgetting object-src 'none', leaving default-src unset so directives silently fall back to permissive defaults, and whitelisting a broad CDN that itself hosts attacker-controllable content. Tighten the source list to the narrowest set that actually works.
For the deployment workflow and how to confirm what a live site is sending, see how to check security headers. CSP also pairs naturally with transport hardening, covered in HSTS explained.
Want to see which directives a domain ships today and where the gaps are? Scan any site's headers free and get a graded report in seconds.
Frequently asked questions
What is a Content Security Policy?
A Content Security Policy is a response header that whitelists the sources a page may load scripts, styles, images, and other resources from. By refusing anything outside that list, it blocks most cross-site scripting (XSS) even when an attacker manages to inject markup.
What does Content-Security-Policy-Report-Only do?
Report-Only mode evaluates your policy and reports every violation it finds, but it never blocks anything. You use it to watch what a new policy would break before you enforce it, making rollout safe.
What is the difference between CSP and a nonce?
A nonce is not a separate thing from CSP; it is a per-request random token placed inside the CSP and on a matching script tag. Only inline scripts carrying that exact token are allowed to run, so you keep specific inline code without opening the door to all of it.