Web Security

CSP (Content Security Policy)

About 4 min read

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, so base-uri, form-action, and frame-ancestors remain unset even if you write default-src 'none', and have to be specified individually
  • script-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 nonces
  • style-src: Controls CSS loading sources
  • img-src: Controls image loading sources
  • connect-src: Controls destinations for fetch, XMLHttpRequest, and WebSocket connections
  • font-src: Controls web font loading sources
  • frame-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 to X-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 in script-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.
Share

Related Terms

Related Articles