Content Security Policy (CSP)

The most powerful browser-level defense against XSS and code injection attacks.

Quick Summary

  • CSP is an HTTP header that declares which content sources the browser may load.
  • It prevents XSS by blocking inline scripts and restricting allowed domains.
  • Key directives include default-src, script-src, style-src, and connect-src.
  • Always start with Report-Only mode to avoid breaking your site.

Introduction

Cross-Site Scripting (XSS) remains one of the most common web vulnerabilities. Despite decades of awareness, it continues to appear in security audits and enables some of the most damaging web attacks. Content Security Policy (CSP) is the browser's most powerful built-in defense against XSS.

A CSP is an HTTP response header that tells the browser exactly which sources of content are allowed to load on your page. Scripts from unapproved sources are blocked automatically, even if an attacker manages to inject malicious code into your HTML. This makes CSP a critical defense-in-depth layer that protects users even when application-level security fails.

This guide explains how CSP works, which directives you need, how to implement it without breaking your site, and the common mistakes that render CSP ineffective. For detailed browser specifics, consult the MDN Web Docs on CSP.

What Is a Content Security Policy?

The Power of the Whitelist

A Content Security Policy (CSP) is an HTTP response header that lets you declare exactly which sources of content the browser is allowed to load. Any content from undeclared sources is automatically blocked by the browser.

This severely limits the damage an attacker can do even if they manage to inject malicious code into your page. The browser simply refuses to execute it if the source is not on the whitelist.

Basic CSP Header

Content-Security-Policy: default-src 'self'; script-src 'self' https://analytics.example.com; img-src 'self' data: https://cdn.example.com

Only loads resources from your domain, analytics from one approved source, and images from your CDN. Everything else is blocked.

Why CSP Matters

  • XSS prevention: CSP blocks the execution of injected scripts, neutralizing the most common web vulnerability category.
  • Supply chain protection: If a third-party CDN is compromised, CSP prevents the modified script from exfiltrating data to unauthorized domains.
  • Compliance: Security frameworks (SOC 2, PCI-DSS) and vendor security reviews expect CSP as a standard security measure.
  • Data exfiltration prevention: The connect-src directive prevents malicious scripts from sending stolen data to attacker servers.
  • GDPR technical measure: CSP is considered an “appropriate technical measure” under GDPR Article 32, helping meet the requirement for data protection by design.

How CSP Prevents XSS Attacks

Blocking Malicious Execution

How CSP Blocks Unauthorized ScriptsYour page loadsBrowser reads CSP headerAllowed source ✓script-src 'self'Script executesUnknown source ✗evil.com/malware.jsBLOCKED by CSP

Cross-Site Scripting (XSS) occurs when an attacker tricks the browser into executing malicious JavaScript. A strong CSP mitigates this by:

  • Blocking inline scripts: Injected <script>malicious()</script> tags are dropped because inline scripts are not in the whitelist.
  • Restricting domains: Only scripts from explicitly whitelisted origins can execute. A script loaded from evil.com is blocked automatically.
  • Preventing eval(): Dynamic code execution functions like eval() and new Function() are blocked by default.
  • Blocking data exfiltration: Even if a script executes, connect-src prevents it from sending data to unauthorized servers.

Don't Use unsafe-inline

Adding 'unsafe-inline' to your CSP defeats its primary purpose. Use nonces or hashes to whitelist specific inline scripts instead.

CSP Directives Reference

Understanding Directives

DirectiveControlsExample
default-srcFallback for all resource types'self'
script-srcJavaScript files'self' https://cdn.example.com
style-srcCSS stylesheets'self' 'unsafe-inline'
img-srcImages'self' data: https://images.example.com
connect-srcXHR, Fetch, WebSocket'self' https://api.example.com
font-srcWeb fonts'self' https://fonts.gstatic.com
frame-ancestorsWho can embed your page'none'
base-uriRestricts <base> tag URLs'self'
form-actionWhere forms can submit'self'
report-uriWhere to send violation reports/csp-report-endpoint

Build a Content Security Policy for your site interactively.

Open CSP Generator

Real-World Examples

The absence of CSP has contributed to major breaches:

  • British Airways (2018): No CSP allowed a Magecart script to capture 380,000 payment cards from the checkout page. CSP would have blocked the unauthorized script and prevented data exfiltration.
  • Ticketmaster (2018): A compromised third-party chatbot widget injected a payment skimmer. CSP with SRI would have blocked the modified script from executing.
  • Newegg (2018): Magecart attackers injected a 15-line payment skimmer that ran undetected for a month. A script-src CSP directive would have blocked the unauthorized domain.

CSP Would Have Prevented All Three

In each case, the attack relied on loading scripts from or exfiltrating data to unauthorized domains. A properly configured CSP with strict script-src and connect-src directives would have blocked the attack at the browser level.

How to Check Your CSP

  1. Run a header scan: Use the Security Headers Checker to see if your CSP header is present and review its directives.
  2. Check DevTools: Open Console tab, CSP violations appear as errors with the blocked resource URL and the violated directive.
  3. Set up violation reporting: Add a report-uri or report-to directive to collect violation data in production.
  4. Use CSP Evaluator: Google's CSP Evaluator tool analyzes your policy for common weaknesses and recommendations.

How to Implement CSP

Deployment Strategy

  1. Start with Report-Only: Use Content-Security-Policy-Report-Only first to log violations without blocking content. Monitor for at least one week.
  2. Build your policy: Use the CSP Generator to build a policy interactively.
  3. Deploy incrementally: Start with default-src 'self' and add allowed sources as needed based on violation reports.
  4. Replace inline scripts with nonces: Instead of unsafe-inline, generate a random nonce for each request:

Nonce-Based CSP

Content-Security-Policy: script-src 'nonce-abc123' <script nonce="abc123"> // This script executes because the nonce matches </script> <script> // This script is BLOCKED, no matching nonce </script>

Nginx Configuration

add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' https://api.example.com; frame-ancestors 'none'" always;

Best Practices

  1. Start with Report-Only mode, deploy in report-only for at least a week before enforcing to avoid breaking functionality.
  2. Use nonces over domain whitelists, cryptographic nonces are more secure because domain whitelists can be bypassed if a CDN is compromised.
  3. Set frame-ancestors, use frame-ancestors 'none' to replace X-Frame-Options with the modern CSP-based equivalent.
  4. Monitor violations in production, set up a reporting endpoint to catch policy violations. This helps you detect both misconfiguration and attempted attacks.
  5. Restrict connect-src, this directive prevents data exfiltration. Only whitelist API endpoints that your application actually uses.
  6. Combine with other security headers, CSP works best alongside HSTS, Referrer-Policy, and Permissions-Policy.

Common Mistakes

  • Using 'unsafe-inline': This allows any inline script to execute, completely defeating CSP's XSS protection. Use nonces or hashes instead.
  • Using 'unsafe-eval': Required by some older frameworks, but opens the door to code injection via eval(). Migrate to frameworks that do not require it.
  • Whitelisting entire CDN domains: If you whitelist https://cdn.jsdelivr.net, an attacker can host malicious code on the same CDN. Use SRI hashes alongside domain whitelists.
  • Not setting default-src: Without a default-src fallback, resource types without specific directives have no restrictions.
  • Deploying without Report-Only first: Enforcing an untested CSP can break your entire site. Always test in report-only mode first.
  • Forgetting about WebSockets: If your app uses WebSockets, you need to include the WebSocket URL in connect-src.

Conclusion

Content Security Policy is the single most effective browser-level defense against XSS and supply-chain attacks. It is not a replacement for secure coding practices, but it catches the attacks that slip through. Every website should have a CSP, the question is not whether to implement one, but how strict to make it.

Start with Report-Only mode, build your policy incrementally using the CSP Generator, and monitor violations in production. Combined with other security headers, CSP creates a robust defense-in-depth strategy.

Scan Your Website

Scan your website with SitePrivacyScore to detect CSP issues automatically. Our free scanner checks your Content Security Policy and identifies weaknesses and missing directives.

Related Guides

Frequently Asked Questions

Does CSP replace input validation?+
No. CSP is a defense-in-depth layer. You must still implement input validation, output encoding, and proper templating to prevent XSS at the application level.
Why is my third-party widget breaking with CSP?+
Third-party widgets often load scripts, stylesheets, and images from multiple domains. You must add all required domains to the appropriate directives (script-src, style-src, connect-src).
What does 'unsafe-inline' do?+
It allows inline <script> and <style> tags to execute. This defeats the primary purpose of CSP against XSS. Avoid it and use nonces or hashes instead.
How do I test CSP without breaking my site?+
Use Content-Security-Policy-Report-Only header first. It logs violations without blocking them, letting you see what would break before enforcing the policy.
Can CSP protect against Magecart attacks?+
Yes. A strict CSP prevents unauthorized scripts from executing and blocks data exfiltration to non-whitelisted domains. This directly mitigates Magecart-style supply chain attacks.
Do I need different CSP for different pages?+
Ideally yes. API endpoints, admin pages, and public pages have different resource requirements. Use targeted policies per-route if your framework supports it.

Verify Your CSP Configuration

A misconfigured CSP can break your site or leave it vulnerable. Scan your domain to check if your policy is correctly structured.

For deeper runtime checks, run the full privacy audit →