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
Start with a report-only CSP
Deploy in report-only mode first to see what would be blocked without actually blocking it.
// 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-reportMonitor reports for a week before switching to enforcing mode. This prevents accidentally breaking your site.
Define your CSP directives
Build your policy by specifying allowed sources for each resource type.
// 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('; ')Add nonce-based script loading
Generate a unique nonce per request to allow specific inline scripts.
// 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>Handle third-party scripts
Add domains for analytics, payment forms, and other third-party resources.
// 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('; ')Create a CSP violation report endpoint
Collect reports of CSP violations to detect attacks and misconfigurations.
// 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 })
}Switch from report-only to enforcing
After monitoring reports and fixing violations, switch to the enforcing header.
// 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