Back to Blog
CSPSecurityXSSWeb Development

Content Security Policy: Protecting Against XSS

Implement CSP to prevent XSS attacks. Learn directives, reporting, and deployment strategies.

B
Bootspring Team
Engineering
February 27, 2026
3 min read

Content Security Policy (CSP) is a powerful defense against XSS and injection attacks.

Basic CSP Header#

1// Express middleware 2app.use((req, res, next) => { 3 res.setHeader( 4 'Content-Security-Policy', 5 "default-src 'self'; script-src 'self'; style-src 'self'" 6 ); 7 next(); 8});

Common Directives#

default-src - Fallback for other directives script-src - JavaScript sources style-src - CSS sources img-src - Image sources font-src - Font sources connect-src - XHR, WebSocket, fetch sources frame-src - iframe sources object-src - plugins (Flash, etc.) base-uri - <base> tag restrictions form-action - Form submission targets frame-ancestors - Who can embed this page

Practical CSP Examples#

1// Strict CSP for modern apps 2const csp = [ 3 "default-src 'self'", 4 "script-src 'self'", 5 "style-src 'self' 'unsafe-inline'", // Often needed for CSS-in-JS 6 "img-src 'self' data: https:", 7 "font-src 'self'", 8 "connect-src 'self' https://api.example.com", 9 "frame-ancestors 'none'", 10 "base-uri 'self'", 11 "form-action 'self'", 12].join('; '); 13 14// With external resources 15const cspWithExternal = [ 16 "default-src 'self'", 17 "script-src 'self' https://cdn.jsdelivr.net", 18 "style-src 'self' https://fonts.googleapis.com", 19 "font-src 'self' https://fonts.gstatic.com", 20 "img-src 'self' data: https://images.example.com", 21 "connect-src 'self' https://api.example.com wss://ws.example.com", 22].join('; ');

Nonce-Based CSP#

1import crypto from 'crypto'; 2 3app.use((req, res, next) => { 4 // Generate unique nonce per request 5 const nonce = crypto.randomBytes(16).toString('base64'); 6 res.locals.nonce = nonce; 7 8 res.setHeader( 9 'Content-Security-Policy', 10 `script-src 'self' 'nonce-${nonce}'` 11 ); 12 13 next(); 14}); 15 16// In template 17<script nonce="<%= nonce %>"> 18 // Inline script allowed with matching nonce 19</script>

Hash-Based CSP#

1import crypto from 'crypto'; 2 3// Hash inline scripts at build time 4const inlineScript = "console.log('Hello')"; 5const hash = crypto 6 .createHash('sha256') 7 .update(inlineScript) 8 .digest('base64'); 9 10// CSP header 11`script-src 'self' 'sha256-${hash}'` 12 13// In HTML 14<script>console.log('Hello')</script>

Next.js CSP#

1// next.config.js 2const cspHeader = ` 3 default-src 'self'; 4 script-src 'self' 'unsafe-eval' 'unsafe-inline'; 5 style-src 'self' 'unsafe-inline'; 6 img-src 'self' blob: data:; 7 font-src 'self'; 8 object-src 'none'; 9 base-uri 'self'; 10 form-action 'self'; 11 frame-ancestors 'none'; 12`; 13 14module.exports = { 15 async headers() { 16 return [ 17 { 18 source: '/(.*)', 19 headers: [ 20 { 21 key: 'Content-Security-Policy', 22 value: cspHeader.replace(/\n/g, ''), 23 }, 24 ], 25 }, 26 ]; 27 }, 28};

CSP Reporting#

1// Report-only mode (doesn't block, just reports) 2res.setHeader( 3 'Content-Security-Policy-Report-Only', 4 "default-src 'self'; report-uri /api/csp-report" 5); 6 7// Reporting endpoint 8app.post('/api/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => { 9 const report = req.body['csp-report']; 10 11 console.log('CSP Violation:', { 12 blockedUri: report['blocked-uri'], 13 violatedDirective: report['violated-directive'], 14 documentUri: report['document-uri'], 15 }); 16 17 res.status(204).end(); 18}); 19 20// Modern reporting API 21`report-to csp-endpoint` 22 23// Report-To header 24res.setHeader('Report-To', JSON.stringify({ 25 group: 'csp-endpoint', 26 max_age: 10886400, 27 endpoints: [{ url: '/api/csp-report' }], 28}));

Deployment Strategy#

1// 1. Start with report-only 2'Content-Security-Policy-Report-Only': "default-src 'self'" 3 4// 2. Analyze reports, adjust policy 5 6// 3. Gradually tighten 7// Week 1: Allow everything you need 8// Week 2: Remove 'unsafe-inline' for scripts 9// Week 3: Remove 'unsafe-eval' 10 11// 4. Enable enforcement 12'Content-Security-Policy': "final-policy-here" 13 14// 5. Keep report-uri for monitoring

Common Issues#

1// ❌ Error: Inline event handlers blocked 2<button onclick="doSomething()">Click</button> 3 4// ✅ Use addEventListener instead 5document.querySelector('button').addEventListener('click', doSomething); 6 7// ❌ Error: eval() blocked 8eval(userCode); 9 10// ✅ Avoid eval, or add 'unsafe-eval' (not recommended) 11 12// ❌ Error: Inline styles blocked 13<div style="color: red">Text</div> 14 15// ✅ Use classes or 'unsafe-inline' for styles 16// (Less risky than script 'unsafe-inline')

CSP significantly reduces XSS risk when implemented properly.

Share this article

Help spread the word about Bootspring