What Is Content Security Policy (CSP)?

Content Security Policy is one of the most powerful browser security mechanisms available. It lets you tell the browser exactly which resources your page is allowed to load — and blocks everything else.


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.

http
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.

DirectiveControlsExample value
default-srcFallback for all resource types not explicitly listed'self'
script-srcJavaScript sources'self' https://cdn.example.com
style-srcCSS stylesheets'self' https://fonts.googleapis.com
img-srcImages and favicons'self' data: https:
connect-srcFetch, XHR, WebSocket, EventSource'self' https://api.example.com
font-srcWeb fonts'self' https://fonts.gstatic.com
frame-srcIframe sources your page loads'none'
frame-ancestorsWho can embed your page'none'
object-srcFlash, plugins'none'
base-uriBase element URLs'self'
report-uri / report-toWhere to send violation reportsURL 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' — allows eval() 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.
Avoid unsafe-inline in script-srcUsing '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.

http
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-violation-report
Always Start with Report-OnlyDeploy Content-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.

http
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.

Related Articles