Content Security Policy: Defending Against XSS and Data Injection

Content Security Policy is one of the most effective defenses against cross-site scripting and data injection attacks. When configured correctly, CSP acts as a safety net that prevents malicious content from executing even if an attacker manages to inject script into your page.
Understanding CSP Directives
CSP works through a set of directives that control which resources the browser is allowed to load. Each directive maps to a resource type — script-src for JavaScript, style-src for CSS, img-src for images, connect-src for XHR and fetch requests, and frame-src for iframes.
Content-Security-Policy: default-src 'self';
script-src 'self' https://analytics.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' https://*.cdn.example.com data:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.soninow.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
default-src serves as the fallback for any directive not explicitly specified. Setting it to 'self' is a strong baseline, then override individual directives as needed. The frame-ancestors directive controls clickjacking — 'none' prevents your site from being embedded in any iframe.
Strict CSP with Nonces
A strict CSP avoids 'unsafe-inline' by using nonces — cryptographically random values generated per request and attached to allowed inline scripts. The browser executes only scripts whose nonce attribute matches the header value.
<!-- Server generates a fresh nonce per request -->
<script nonce="a1b2c3d4e5f6">
initializeApp();
</script>
// Express middleware generating CSP nonces
import crypto from 'crypto';
app.use((req, res, next) => {
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.cspNonce = nonce;
res.setHeader('Content-Security-Policy', [
`default-src 'self'`,
`script-src 'self' 'nonce-${nonce}'`,
`style-src 'self' 'nonce-${nonce}'`,
`img-src 'self' https: data:`,
`base-uri 'self'`,
`form-action 'self'`,
].join('; '));
next();
});
Nonces must be unique for each request and never reused. For applications with server-side rendering, templating engines can inject the nonce directly into script and style tags. Hash-based allowlisting is an alternative — compute the SHA hash of the script content and include it in the script-src directive using 'sha256-...'.
CSP Reporting and Monitoring
CSP violations happen — sometimes from legitimate application features you forgot to account for. Configure a report-uri or the newer report-to directive to receive violation reports at a dedicated endpoint.
Content-Security-Policy-Report-Only: default-src 'self';
script-src 'self';
report-uri /csp-report;
Use Content-Security-Policy-Report-Only during initial deployment to gather violation data without blocking resources. Once you are confident the policy covers all legitimate resources, switch to the enforcement header Content-Security-Policy. Collect reports using a dedicated service or a simple endpoint that logs to your observability platform:
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
const report = req.body?.['csp-report'];
logger.warn('CSP violation', {
blockedUri: report?.['blocked-uri'],
violatedDirective: report?.['violated-directive'],
sourceFile: report?.['source-file'],
userAgent: req.headers['user-agent'],
});
res.status(204).end();
});
Third-Party Script and Style Management
Every third-party script integrated into your application — analytics, chat widgets, payment forms, A/B testing tools — expands your CSP surface area. Use subresource integrity (SRI) attributes to ensure third-party scripts haven't been tampered with:
<script
src="https://cdn.example.com/widget.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"
></script>
When possible, load third-party scripts from subdomains you control, proxying through your own infrastructure rather than allowing https://*.example.com in your CSP.
CSP for Single-Page Applications
Single-page applications present unique CSP challenges because they often use inline event handlers, eval-based code splitting, and dynamic style injection. Modern frameworks generate proper CSP-compatible markup, but bundles may still include eval() dependencies.
Configure Webpack or Vite to avoid eval() in development and production:
// Webpack: avoid eval in CSP-unfriendly modes
module.exports = {
devtool: 'source-map', // Instead of 'eval'
output: {
crossOriginLoading: 'anonymous',
},
};
Common CSP Pitfalls
- Using
'unsafe-inline'inscript-srcdefeats CSP's primary protection against XSS. Eliminate it through nonces or hashes. - Forgetting to update CSP when adding new third-party services leads to blocked resources and broken functionality.
- Setting
default-src 'none'without explicit directives for each needed resource type creates a restrictive policy that breaks the application. - Neglecting
base-uriallows attackers to override the document base URL and hijack relative script loads. - Overly permissive
connect-srcpermits data exfiltration to arbitrary endpoints.
Deploying CSP Incrementally
Start with a restrictive Report-Only policy, collect reports for a week, analyze blocked resources, adjust the policy, and then enforce. Repeat this cycle as your application evolves. Tools like CSP Evaluator (by Google) and the Mozilla Observatory score your policy and highlight weaknesses.
A well-configured Content Security Policy is one of the highest-impact security improvements you can make. Our <a href="/services/web-development">web development team</a> implements and tunes CSP for all projects, ensuring protection without breaking functionality. Contact us to evaluate your current security headers.
Related Insights

API Rate Limiting Strategies: Token Bucket, Leaky Bucket, and Sliding Window
A guide to implementing API rate limiting including token bucket, leaky bucket, sliding window, and distributed rate limiting with Redis for production APIs.

Authentication Patterns in Modern Web Apps: JWT, OAuth, and Session Management
A guide to authentication patterns for web applications including JWT implementation, OAuth 2.0 flows, refresh tokens, session management, and secure storage.

Authentication Patterns in Modern Web Apps: JWT, Sessions, and Passkeys
A guide to modern authentication patterns comparing JWT, session-based auth, and passkeys including implementation strategies, security considerations, and user experience.