Step-by-Step Guide
6 steps

How to Add a Content Security Policy Header

Content Security Policy (CSP) is the most effective defense against XSS attacks. It tells the browser exactly which resources are allowed to load and execute. This guide covers building a CSP from scratch, handling common edge cases, and deploying without breaking your app.

Find security issues automatically before attackers do.

Follow These Steps

1

Start with a report-only CSP

Deploy in report-only mode first to see what would be blocked without actually blocking it.

Code Example
// Start with Content-Security-Policy-Report-Only
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; report-uri /api/csp-report

Monitor reports for a week before switching to enforcing mode. This prevents accidentally breaking your site.

2

Define your CSP directives

Build your policy by specifying allowed sources for each resource type.

Code Example
// Recommended starting CSP
const csp = [
  "default-src 'self'",           // Default: only same origin
  "script-src 'self'",            // Scripts: only same origin
  "style-src 'self' 'unsafe-inline'",  // Styles: self + inline (needed for many frameworks)
  "img-src 'self' data: https:",  // Images: self, data URIs, any HTTPS
  "font-src 'self'",              // Fonts: only same origin
  "connect-src 'self' https://api.yourdomain.com",  // XHR/fetch: self + your API
  "frame-ancestors 'none'",       // No framing (replaces X-Frame-Options)
  "base-uri 'self'",              // Restrict <base> tag
  "form-action 'self'"            // Restrict form submissions
].join('; ')
3

Add nonce-based script loading

Generate a unique nonce per request to allow specific inline scripts.

Code Example
// Server-side: generate nonce per request
const nonce = crypto.randomUUID()
const csp = `script-src 'self' 'nonce-${nonce}'`

// HTML: apply nonce to script tags
<script nonce={nonce}>console.log("allowed")</script>
<script>console.log("blocked by CSP")</script>
4

Handle third-party scripts

Add domains for analytics, payment forms, and other third-party resources.

Code Example
// Common third-party additions:
const csp = [
  // Google Analytics
  "script-src 'self' https://www.googletagmanager.com",
  "connect-src 'self' https://www.google-analytics.com",
  
  // Stripe
  "script-src 'self' https://js.stripe.com",
  "frame-src https://js.stripe.com https://hooks.stripe.com",
  
  // Google Fonts
  "style-src 'self' https://fonts.googleapis.com",
  "font-src 'self' https://fonts.gstatic.com"
].join('; ')
5

Create a CSP violation report endpoint

Collect reports of CSP violations to detect attacks and misconfigurations.

Code Example
// app/api/csp-report/route.ts
export async function POST(req: Request) {
  const report = await req.json()
  console.warn('CSP Violation:', JSON.stringify(report))
  // Optionally send to monitoring service
  return new Response(null, { status: 204 })
}
6

Switch from report-only to enforcing

After monitoring reports and fixing violations, switch to the enforcing header.

Code Example
// Change from:
Content-Security-Policy-Report-Only: ...

// To:
Content-Security-Policy: ...

Keep report-uri in the enforcing policy to continue monitoring for violations.

What You'll Achieve

Your application has a Content Security Policy that blocks XSS attacks, restricts resource loading to trusted sources, and reports violations for monitoring.

Common Mistakes to Avoid

Mistake

Using unsafe-inline and unsafe-eval in script-src

Fix

These defeat the purpose of CSP. Use nonce-based or hash-based policies instead of unsafe-inline. Refactor code to avoid eval().

Mistake

Deploying CSP in enforcing mode without testing

Fix

Always start with Content-Security-Policy-Report-Only. Monitor violations for at least a week before enforcing.

Mistake

Setting an overly permissive default-src

Fix

Never use default-src *. Start with default-src 'self' and explicitly allow additional sources per directive.

Frequently Asked Questions

Will CSP break my website?

If deployed without testing, yes. Always start with report-only mode to identify what would be blocked before enforcing the policy.

Do I still need X-Frame-Options if I have CSP frame-ancestors?

CSP frame-ancestors supersedes X-Frame-Options, but keep both for backward compatibility with older browsers that do not support CSP.

How do I allow inline styles with CSP?

Add unsafe-inline to style-src. While less ideal than nonce-based styles, most CSS frameworks require inline styles and unsafe-inline for style-src is acceptable.

Ready to Secure Your App?

VAS automatically scans your deployed app for the security issues covered in this guide. Get actionable results in minutes.

Start Security Scan