Safeguard
Security Guides

Content Security Policy (CSP) Explained (2026)

A Content Security Policy is your last line of defense against XSS. Here is how CSP works, why nonce-based strict policies beat allowlists, and how to deploy one without breaking your app.

Daniel Osei
Security Researcher
Updated 6 min read

A Content Security Policy is an HTTP header that tells the browser which sources of script, style, and other content it is allowed to load and execute — in short, the CSP meaning in security is a browser-enforced allowlist for executable content, not a scanner or a firewall rule. Its primary job is to contain cross-site scripting: even if an attacker manages to inject markup into your page, a well-built CSP stops the injected script from running. That makes CSP a genuine last line of defense, the control that turns a would-be account takeover into a blocked, reported non-event. It is also one of the most misconfigured headers on the web, because the easy way to write a CSP, an allowlist of trusted hosts, is the way that attackers routinely bypass.

The short answer for 2026: deploy a strict, nonce-based CSP with strict-dynamic, avoid unsafe-inline and host allowlists, roll it out first in report-only mode, and add Trusted Types to close off DOM-based XSS. Here is how each piece fits.

How CSP works

The browser reads the Content-Security-Policy header and enforces its directives against every resource the page tries to load. Each directive names a resource type and the sources permitted for it. The classic (and weak) approach lists allowed hosts:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; object-src 'none'

The problem with host allowlists is that any allowed host with an open redirect, a JSONP endpoint, or an outdated library becomes a bypass. Research repeatedly shows the majority of allowlist-based policies are trivially bypassable. The modern recommendation is a strict, nonce-based policy instead.

Strict, nonce-based CSP

With a nonce, the server generates a fresh random value per response and stamps it on every script tag it trusts. The browser runs only scripts carrying the correct nonce, so injected script (which cannot know the nonce) never executes. Adding strict-dynamic lets your trusted scripts load further scripts they need, so you do not have to allowlist every CDN.

Content-Security-Policy:
  script-src 'nonce-r4nd0m123' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
  require-trusted-types-for 'script'

The matching markup carries the nonce (note: this is illustrative, and the nonce is regenerated per request):

<script nonce="r4nd0m123" src="/app.js"></script>

Three details make or break a strict CSP. First, the nonce must be unguessable and unique per response; a static or reused nonce is worthless. Second, object-src 'none' and base-uri 'none' close off plugin and base-tag injection vectors that attackers use to sidestep script controls. Third, never add 'unsafe-inline' to script-src; when a nonce is present modern browsers ignore 'unsafe-inline' anyway, but adding it out of habit signals a policy that was copied rather than designed.

Roll out in report-only mode

Do not enforce a new CSP blind. Ship it first as report-only, which reports violations without blocking anything, so you can see what real traffic would break before it does.

Content-Security-Policy-Report-Only:
  script-src 'nonce-r4nd0m123' 'strict-dynamic';
  object-src 'none';
  report-to csp-endpoint

Collect reports for a representative period, fix the legitimate sources that trip the policy (usually inline handlers and third-party widgets), then switch the header to enforcing. Keep a reporting endpoint even after enforcement so you get an early signal when an injection attempt or a broken deploy occurs.

Close DOM XSS with Trusted Types

CSP historically controlled where scripts came from, not the dangerous DOM sinks like innerHTML that cause DOM-based XSS. Trusted Types, enabled with require-trusted-types-for 'script', forces those sinks to receive a typed, sanitized value instead of a raw string, eliminating a whole category of client-side injection at the browser level.

CSP checklist

DoAvoid
Use per-response nonces with strict-dynamicHost allowlists as your primary control
Set object-src 'none' and base-uri 'none'Adding 'unsafe-inline' to script-src
Roll out with report-only firstEnforcing a brand-new policy blind
Keep a live reporting endpointDisabling reporting after go-live
Add Trusted Types for DOM sinksAssuming CSP alone stops DOM XSS

How Safeguard helps

A CSP is a runtime property of your deployed pages, so it is best verified from the outside. Safeguard's dynamic application security testing inspects your live responses, flags a missing, weak, or bypassable CSP, and correlates it with the injection findings the policy is meant to contain. Griffin AI explains why a given policy is weak and drafts a strict replacement tailored to the sources your app actually uses, while the Safeguard CLI lets you assert CSP presence against preview builds in CI. Because injected code often rides in through vulnerable front-end packages, Safeguard also runs software composition analysis on your dependency graph, and teams comparing tools can review the pricing.

Ship a CSP that actually holds: get started free or read the documentation.

Frequently Asked Questions

What does a Content Security Policy actually protect against?

Primarily cross-site scripting. A strict CSP tells the browser which scripts are allowed to execute, so even if an attacker injects markup into your page, the injected script lacks the correct nonce and never runs. It also mitigates clickjacking (via frame-ancestors), plugin injection, and base-tag attacks.

Why is a nonce-based CSP better than an allowlist of hosts?

Host allowlists are routinely bypassed: any allowlisted domain with an open redirect, a JSONP endpoint, or a vulnerable script becomes an execution path for injected code. A per-response nonce with strict-dynamic trusts specific script instances rather than whole domains, which removes those bypasses.

How do I deploy CSP without breaking my site?

Start with Content-Security-Policy-Report-Only, which reports violations without blocking. Collect reports across real traffic, fix the legitimate sources that trip the policy, then switch to the enforcing Content-Security-Policy header. Keep a reporting endpoint afterward for early warning of injection attempts and broken deploys.

Does CSP stop DOM-based XSS?

Not by itself, because classic CSP governs where scripts load from, not dangerous DOM sinks like innerHTML. Add require-trusted-types-for 'script' to enable Trusted Types, which forces those sinks to accept only sanitized typed values and eliminates a major class of client-side injection.

What is the CSP meaning in security terms?

In a security context, CSP is short for Content Security Policy — a browser-enforced allowlist, delivered as an HTTP response header, that restricts which script, style, and other resource sources a page is permitted to load and execute. It exists specifically to contain cross-site scripting after an injection has already happened, not to prevent the injection itself.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.