Skip to main content
SecScannerSecScanner
FeaturesCompliancePricingBlogContact
Sign InGet Started
← Back to Blog
HeadersJanuary 29, 202512 min read

Content Security Policy Masterclass: From Basics to Advanced Protection

Master CSP implementation with practical examples. Learn how to build policies that prevent XSS attacks while maintaining site functionality, including nonces, hashes, and report-only mode.

By SecScanner Team
Content Security Policy Masterclass: From Basics to Advanced Protection

Content Security Policy (CSP) is your most powerful defense against cross-site scripting (XSS) attacks. Yet many developers implement it incorrectly, leaving their sites vulnerable or breaking functionality. This guide takes you from CSP basics to advanced techniques used by security-conscious organizations.

Understanding the XSS Threat

Cross-site scripting remains one of the most prevalent web vulnerabilities. Attackers inject malicious scripts that execute in users' browsers, stealing credentials, session tokens, or performing actions on behalf of victims.

How CSP Stops XSS

CSP tells browsers which resources are allowed to load and execute. When an attacker injects a script, the browser blocks it because it wasn't explicitly allowed by your policy. This creates a defense-in-depth layer even if your application has XSS vulnerabilities.

CSP Directive Reference

Source Directives

  • default-src: Fallback for all resource types not explicitly defined
  • script-src: Controls JavaScript execution
  • style-src: Controls CSS loading
  • img-src: Controls image sources
  • font-src: Controls font loading
  • connect-src: Controls fetch, XHR, WebSocket connections
  • media-src: Controls audio and video sources
  • object-src: Controls plugins (Flash, Java applets)
  • frame-src: Controls iframe sources
  • worker-src: Controls Web Worker sources
  • manifest-src: Controls web app manifest

Document Directives

  • base-uri: Restricts URLs for the <base> element
  • sandbox: Enables sandbox mode for the page

Navigation Directives

  • form-action: Restricts form submission targets
  • frame-ancestors: Controls who can embed your page (replaces X-Frame-Options)
  • navigate-to: Restricts navigation targets

Building Your First CSP

Start Restrictive

Begin with the most restrictive policy possible:

Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

This policy only allows resources from your own origin and blocks everything else.

Source Values Explained

  • 'none': Block all resources of this type
  • 'self': Allow resources from the same origin
  • 'unsafe-inline': Allow inline scripts/styles (weakens security significantly)
  • 'unsafe-eval': Allow eval() and similar (avoid at all costs)
  • https: Allow any HTTPS URL
  • data: Allow data: URIs
  • blob: Allow blob: URIs
  • specific-domain.com: Allow resources from specific domain

Advanced Techniques: Nonces and Hashes

Nonce-Based CSP

Nonces allow specific inline scripts without 'unsafe-inline'. Generate a unique nonce for each request:

Content-Security-Policy: script-src 'nonce-abc123xyz789'

Then add the nonce to your script tags:

<script nonce="abc123xyz789">
  // Your inline JavaScript
</script>

Critical: Generate a new cryptographically random nonce for every page load. Never reuse nonces.

Hash-Based CSP

For static inline scripts, use hashes instead of nonces:

Content-Security-Policy: script-src 'sha256-BASE64_HASH_HERE'

Calculate the hash of your script content (without the script tags). The browser will only execute scripts matching the hash.

strict-dynamic

The 'strict-dynamic' keyword allows scripts loaded by trusted scripts to execute, even without explicit allowlisting:

Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'

This is invaluable for applications that dynamically load scripts.

Report-Only Mode: Test Before Deploy

Never deploy CSP blindly. Use report-only mode first:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violations

This header logs violations without blocking resources, letting you identify issues before they break your site.

Setting Up Violation Reporting

Create an endpoint to receive CSP violation reports:

// Example Express.js endpoint
app.post('/csp-violations', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.log('CSP Violation:', req.body['csp-report']);
  res.status(204).end();
});

Using report-to Directive

The newer Reporting API provides more detailed reports:

Report-To: {"group":"csp-endpoint","max_age":86400,"endpoints":[{"url":"/csp-violations"}]}
Content-Security-Policy: default-src 'self'; report-to csp-endpoint

Common CSP Mistakes

Mistake 1: Using 'unsafe-inline' with script-src

This effectively disables XSS protection. Use nonces or hashes instead.

Mistake 2: Overly Permissive Domains

Allowing *.cloudflare.com or *.googleapis.com can let attackers use legitimate services to serve malicious content. Be as specific as possible.

Mistake 3: Forgetting object-src

Always include object-src 'none' to block Flash and other plugins that can execute code.

Mistake 4: Missing base-uri

Without base-uri 'self', attackers can inject a base tag to hijack relative URLs.

Mistake 5: Not Testing Thoroughly

Always use report-only mode and test all site functionality before enforcing.

Framework-Specific Implementation

Next.js

// next.config.js
const cspHeader = `
  default-src 'self';
  script-src 'self' 'nonce-{nonce}' 'strict-dynamic';
  style-src 'self' 'unsafe-inline';
  img-src 'self' blob: data:;
  font-src 'self';
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  frame-ancestors 'none';
`.replace(/\n/g, '');

module.exports = {
  async headers() {
    return [{
      source: '/(.*)',
      headers: [{ key: 'Content-Security-Policy', value: cspHeader }]
    }]
  }
}

Express.js with Helmet

const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.nonce}'`],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:", "https:"],
    objectSrc: ["'none'"],
    upgradeInsecureRequests: []
  }
}));

Real-World CSP Examples

Static Marketing Site

Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' https://images.example.com; font-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'

Single Page Application

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-RANDOM' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com wss://realtime.example.com; object-src 'none'; base-uri 'self'

E-commerce with Third-Party Integrations

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-RANDOM' https://js.stripe.com https://www.google-analytics.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self' https://api.stripe.com https://www.google-analytics.com; frame-src https://js.stripe.com; object-src 'none'; base-uri 'self'

CSP Implementation Checklist

  • Start with default-src 'none' and add only what's needed
  • Always include object-src 'none' and base-uri 'self'
  • Use nonces or hashes instead of 'unsafe-inline' for scripts
  • Never use 'unsafe-eval'
  • Deploy in report-only mode first
  • Set up violation reporting and monitoring
  • Be specific with allowed domains
  • Test all functionality before enforcing
  • Use SecScanner to regularly audit your CSP
  • Review and update CSP when adding new features

A well-implemented CSP significantly reduces your XSS attack surface. Start with report-only mode, iterate based on violations, and gradually tighten your policy. The effort is worth the protection.

Related Articles

Headers

The Complete Guide to HTTP Security Headers

10 min read

Headers

CORS Security: The Complete Guide to Cross-Origin Resource Sharing

11 min read

Headers

Subresource Integrity (SRI): Protecting Your Site from CDN Compromises

8 min read

Check Your Website Security

Want to see how your website measures up? Run a free security scan with SecScanner to identify vulnerabilities and get actionable remediation guidance.

Scan Your Website Free

On This Page

ComplianceTerms of UsePrivacy PolicyRefund PolicyContact

© 2025-2026 SecScanner. All rights reserved.