CSP (Content Security Policy)
About 4 min read
Last updated: 2026-09-01
What Is CSP (Content Security Policy)
CSP (Content Security Policy) is an HTTP response header that allows the server to control the sources from which a web page can load resources (scripts, stylesheets, images, fonts, etc.). Because the browser blocks loading or execution of resources not permitted by CSP, it works as a layer that blocks the execution of scripts injected through XSS (Cross-Site Scripting) and the sending of stolen information to external destinations.
While the fundamental countermeasure for XSS is output escaping, implementation oversights can never be completely eliminated. CSP serves as a defense-in-depth layer that "prevents the attacker's script from executing even if an XSS vulnerability exists." That said, all it can stop is loading and execution you have not permitted; an attack that succeeds within the permitted range still gets through. Among security headers it has the largest number of settings, and how far you manage to narrow them down is exactly what determines its effect in practice. Level 2 of the specification became a W3C Recommendation on December 15, 2016, while Level 3 was still under development as a W3C Working Draft as of August 2026.
Key Directives
CSP consists of multiple directives that specify allowed sources for each resource type.
default-src: Default policy for resources not explicitly specified by other directives.default-src 'self'allows only resources from the same origin. The fallback applies only to the fetch directives, sobase-uri,form-action, andframe-ancestorsremain unset even if you writedefault-src 'none', and have to be specified individuallyscript-src: Controls JavaScript loading sources. The core directive for XSS prevention. Avoid'unsafe-inline'and use nonce or hash instead. The specification ignores'unsafe-inline'as soon as a nonce or hash is present, so listing both is only meaningful for backward compatibility with older implementations that do not interpret noncesstyle-src: Controls CSS loading sourcesimg-src: Controls image loading sourcesconnect-src: Controls destinations for fetch, XMLHttpRequest, and WebSocket connectionsfont-src: Controls web font loading sourcesframe-src: Controls the sources of pages that your own page may load into an iframe (control on the embedding side)frame-ancestors: Works in the opposite direction, controlling which parent page origins may embed your site in an iframe (control on the embedded side). It is the successor toX-Frame-Options, and it is this directive that you specify as a countermeasure against clickjacking
Source Value Specification
'self': Same origin'none': Block all'nonce-{random}': Allow only inline scripts/styles with a specific nonce value'strict-dynamic': Also allows scripts dynamically loaded by scripts that were permitted through a nonce or hash. When it is specified, the domains and schemes listed inscript-src, along with'self'and'unsafe-inline', are ignored when deciding whether a script may load, leaving only nonces and hashes in effect- Specific domains:
https://cdn.example.com
Step-by-Step Deployment
CSP deployment should proceed gradually to avoid breaking existing sites.
Step 1: Monitor with Report-Only Mode
Using the Content-Security-Policy-Report-Only header detects and reports policy violations without blocking resources. Start with this mode to understand what resources your site currently loads.
Step 2: Develop the Base Policy
Based on the report analysis, narrow down the sources for the loading directives such as images and styles. For scripts, however, a 2016 study (Lukas Weichselbaum et al., "CSP Is Dead, Long Live CSP!") documented how JSONP endpoints and open redirects left on permitted domains can become a way around the policy, so the design must not rely on a whitelist of sources alone. The CSP Level 3 specification also mentions, as reference material, a configuration it calls Strict CSP: a nonce or hash combined with 'strict-dynamic' in script-src, plus a narrowed base-uri.
Step 3: Production Deployment and Continuous Monitoring
Switch to the Content-Security-Policy header for production deployment. Continuously collect violation reports with the report-to directive (Level 3 deprecates report-uri and points to this replacement) to address false positives and new resource requirements. Writing both during the transition period keeps reports from being lost in environments that interpret only one of them.
Recommended Strict Policy Example
default-src 'none'; script-src 'self' 'nonce-{random}' 'strict-dynamic'; style-src 'self' 'nonce-{random}'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
This policy controls inline scripts with nonces and uses 'strict-dynamic' to also allow dynamically loaded scripts. If you need backward compatibility with browsers that do not interpret 'strict-dynamic', a note in the specification shows that you can also list https: in script-src (browsers that do interpret it will ignore that entry).
CSP Integration with Other Security Headers
All that CSP can decide is whether a resource may be loaded and executed. Combining it with the other security headers lets each of them cover the area it is responsible for.
- Combined with HSTS: Enforces HTTPS. A page delivered over plain HTTP can have the CSP header itself removed or rewritten in transit, so HTTPS secures a delivery path on which the policy reliably arrives
X-Content-Type-Options: nosniff: Prevents MIME type sniffing and blocks execution of resources that should not be interpreted as scripts- Relationship with CORS: The two work in opposite directions. CSP is on the side that restricts the sources your own page may load from, while CORS is on the side that opens up, only as far as the server declares, the reading of responses that the same-origin policy closes by default
What takes effort during deployment is the work of inventorying the existing inline scripts and third-party tags and replacing them with nonces and a tidied-up set of sources. That inventory grows with the number of years a site has been running, so in a new project, building the templates around nonces from the start removes the need to retrofit protection against XSS and clickjacking later.
To learn more about this topic, see HTTP Security Headers: 5 Essential Headers to Protect Your Website.
Common Misconceptions
- Setting up CSP eliminates the need for XSS countermeasures
- CSP is one layer of defense in depth that mitigates XSS damage, not a replacement for the fundamental XSS countermeasure (output escaping). Ways around policies that rely on a whitelist of sources were documented in a 2016 study (Lukas Weichselbaum et al., "CSP Is Dead, Long Live CSP!"), and the CSP Level 3 specification presents a configuration using a nonce or hash together with 'strict-dynamic' as the recommended form. Implementing both output escaping and CSP is the baseline.
- CSP is too difficult to configure and unnecessary for small sites
- Even small sites face XSS risks. Starting with a minimal policy (e.g., default-src 'self'; script-src 'self') makes deployment easy. You can gradually tighten it while checking impact with Report-Only mode.