Content Security Policy (CSP) is an HTTP response header that instructs the browser about which content sources are trusted. By explicitly allowlisting the origins that can supply scripts, styles, images, and other resources, CSP eliminates entire categories of injection attacks that would otherwise require complex server-side defences.
What Attacks Does CSP Prevent?
Cross-Site Scripting (XSS)
XSS is the most common web application vulnerability. An attacker injects malicious script into a page — through a comment field, URL parameter, or compromised third-party library — and the victim's browser executes it with full access to cookies, session tokens, and DOM content.
A strong CSP header breaks this attack by blocking inline scripts and restricting external scripts to explicitly approved hosts. Even if an attacker succeeds in injecting a<script> tag, the browser refuses to run it because the source was not in the policy.
Data Injection Attacks
Attackers can use injected content to silently exfiltrate data — for example, by loading an image from an attacker-controlled server with the stolen data encoded in the URL. CSP'sconnect-src and img-src directives restrict where the browser may send or fetch data, cutting off this exfiltration channel.
Clickjacking (via frame-ancestors)
The frame-ancestors directive specifies which pages are allowed to embed your site in a <frame> or <iframe>. This replaces and extends the olderX-Frame-Options header with more granular control.
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'Core CSP Directives
Each directive controls a specific category of resource. Directives not listed in the policy fall back to default-src.
| Directive | Controls | Example value |
|---|---|---|
default-src | Fallback for all resource types not explicitly listed | 'self' |
script-src | JavaScript sources | 'self' https://cdn.example.com |
style-src | CSS stylesheets | 'self' https://fonts.googleapis.com |
img-src | Images and favicons | 'self' data: https: |
connect-src | Fetch, XHR, WebSocket, EventSource | 'self' https://api.example.com |
font-src | Web fonts | 'self' https://fonts.gstatic.com |
frame-src | Iframe sources your page loads | 'none' |
frame-ancestors | Who can embed your page | 'none' |
object-src | Flash, plugins | 'none' |
base-uri | Base element URLs | 'self' |
report-uri / report-to | Where to send violation reports | URL of your reporting endpoint |
Source Values: What Can Go in a Directive?
Directives accept one or more source expressions separated by spaces:
'self'— the same origin (scheme, host, and port).'none'— blocks all sources of that type.https://example.com— a specific host.https:— any HTTPS source (broad; avoid if possible).'unsafe-inline'— allows inline scripts or styles (weakens CSP significantly).'unsafe-eval'— allowseval()and similar dynamic code execution.'nonce-BASE64'— allows a specific inline script or style identified by a one-time token.'sha256-HASH'— allows an inline block whose SHA-256 hash matches.
'unsafe-inline' in script-src largely defeats the purpose of CSP, because it allows any inline script to execute — including injected ones. Use nonces or hashes instead. The 'unsafe-inline' keyword is ignored when a nonce or hash is present in the directive.Report-Only Mode
Before enforcing a policy, you can deploy it in observation mode using theContent-Security-Policy-Report-Only header. In this mode the browser does not block anything, but it sends a violation report to your specified endpoint for every resource that would have been blocked. This lets you audit what your site loads and refine your policy without any risk of breaking functionality.
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-violation-reportContent-Security-Policy-Report-Only for at least a week before switching to enforcement mode. Collect and review violation reports to catch legitimate third-party resources you may have missed. See the guide on how to configure a CSP for a step-by-step workflow.Nonces and Hashes for Inline Scripts
Modern applications often need some inline scripts or styles. Rather than enabling'unsafe-inline', CSP offers two safer alternatives:
Nonces
A nonce (number used once) is a cryptographically random token generated per HTTP response. Your server includes it in the CSP header and as an attribute on the specific inline script elements that are intentionally present. The browser only executes inline scripts whose nonce attribute matches the header value.
Content-Security-Policy: script-src 'nonce-rAnd0mBase64=='Hashes
If your inline script content never changes, you can compute its SHA-256 hash and include that in the policy. The browser checks the hash of each inline script against the allowlist before executing it. This is particularly useful for small static scripts you control entirely.
Browser Support
CSP Level 2 is supported by all modern browsers including Chrome, Firefox, Safari, and Edge. CSP Level 3 (which adds 'strict-dynamic' and other refinements) is supported in Chrome and Firefox and partially in Safari. The original CSP Level 1 is supported even in Internet Explorer 11 (via the X-Content-Security-Policy vendor prefix, though support is limited). For most deployments, targeting CSP Level 2 provides full cross-browser coverage.
Frequently Asked Questions
Does CSP replace other security headers?
CSP complements other headers rather than replacing them. You should still useX-Frame-Options for legacy browser support, X-Content-Type-Options to prevent MIME sniffing, and Strict-Transport-Security for HTTPS enforcement. CSP adds a powerful layer on top but does not make the others redundant.
Can CSP break my website?
Yes, an overly strict CSP will block legitimate resources. Always deploy inReport-Only mode first, analyse violations, and refine your policy before enforcing it. Monitoring violation reports after enforcement is also important to catch edge cases.
What is 'strict-dynamic'?
The 'strict-dynamic' source expression (CSP Level 3) allows scripts loaded by a trusted nonce-verified script to also run, without needing to be individually listed. This enables modern JavaScript module loading patterns without 'unsafe-inline'.
How do I test my CSP?
The browser console shows CSP violation messages in real time. You can also use the ShowDNS Security Headers scanner to check your live policy and the HTTP Header Checker to inspect the raw header value.