SecurityBy Sam Holloway··7 min read

What Is a Content Security Policy and How Do You Build One?

A Content Security Policy prevents XSS attacks by telling browsers which resources are allowed to load on your page. A practical guide to writing and deploying your first CSP.

Cross-site scripting (XSS) is consistently one of the most common web vulnerabilities, appearing in the OWASP Top 10 every year. An attacker who finds an XSS vulnerability can inject malicious scripts into your pages, stealing cookies, session tokens, and form data from your users. A Content Security Policy (CSP) is a browser-enforced defence that can dramatically reduce the damage an XSS attack can cause, even when the vulnerability exists.

How CSP works

A Content Security Policy is delivered as an HTTP response header (Content-Security-Policy) or a meta tag, and tells the browser a whitelist of approved sources for every type of resource your page can load: scripts, stylesheets, images, fonts, frames, and more. When the browser encounters a resource request that does not match the whitelist, it blocks it and (optionally) reports the violation. An attacker who injects a script tag pointing to their server will find it blocked by the browser, even if the injection itself succeeded.

The directives you need to know

  • default-src: the fallback for any resource type not explicitly listed. Setting default-src 'self' means only resources from your own origin are allowed by default.
  • script-src: controls which scripts can execute. This is the most critical directive for XSS prevention. 'self' allows your own scripts; 'unsafe-inline' allows inline scripts (weakens the policy significantly).
  • style-src: controls stylesheets. Like script-src, 'unsafe-inline' allows inline styles but weakens the policy.
  • img-src: controls image sources. data: allows inline data URIs for images, which is often needed.
  • connect-src: controls which URLs can be reached via fetch, XMLHttpRequest, and WebSocket.
  • font-src: controls font file sources.
  • frame-src: controls which origins can be embedded in iframes.
  • report-uri or report-to: a URL where violation reports are sent, letting you discover blocked resources without users telling you.

A practical starting policy

A reasonable starting CSP for a server-rendered web app with no inline scripts or styles: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'. This blocks almost all XSS vectors and prevents your page from being embedded in iframes (which prevents clickjacking). It will likely need adjustment for any third-party scripts (analytics, chat widgets, payment processors) your page loads.

The inline script problem

The most common obstacle when implementing CSP is inline scripts. If your page includes <script>...</script> blocks or onclick handlers, a strict CSP that excludes 'unsafe-inline' will break them. The correct solution is to move inline scripts to external files. If that is not feasible, CSP provides two alternatives: nonces (a cryptographically random value added to both the CSP header and the script tag, valid for a single page load) and hashes (a SHA-256 hash of the script content added to the header, allowing that specific script to run).

Report-only mode for testing

Deploying a new CSP directly can break functionality if you have not accounted for all resource sources. The Content-Security-Policy-Report-Only header lets you test a CSP without enforcing it: violations are reported to your report-uri endpoint but resources are not blocked. This allows you to audit what your existing page loads before committing to enforcement. Run in report-only mode for a week or two, fix the violations you care about, and then switch to enforcement.

Third-party scripts and CSP

Adding a third-party script (Google Analytics, Stripe, Intercom, etc.) typically requires adding that provider's domain to script-src and often connect-src, img-src, and frame-src as well. Each addition to the whitelist is a potential attack surface if the third-party domain itself is compromised. This is not a reason to avoid CSP: a narrow whitelist is far better than no policy at all: but it is a reason to audit which third-party scripts are actually necessary.

AlteredIdea's CSP Builder generates a Content-Security-Policy header from a visual interface: select your directives, add your allowed sources, and get the ready-to-deploy header string. All generated in your browser.

S

Sam Holloway

Sam is a technical writer and developer who has spent over a decade building web tools and writing about software, security, and the web platform. He focuses on making complex topics genuinely useful for working developers and non-technical users alike.

Try it yourself

CSP Builder

Free, browser-based: your files never leave your device.

Open tool