DEV Community

Cover image for πŸ›‘οΈ Content Security Policy (CSP): A Complete Guide for Developers
Fazal Mansuri
Fazal Mansuri

Posted on

πŸ›‘οΈ Content Security Policy (CSP): A Complete Guide for Developers

Security is one of the most critical aspects of modern web development. Even if your code is correct, attackers may exploit vulnerabilities like Cross-Site Scripting (XSS) or Clickjacking. That’s where Content Security Policy (CSP) comes in.

This blog covers:
βœ… What CSP is & how it works
βœ… All major directives (default-src, script-src, img-src, etc.) explained in detail
βœ… Real-world examples
βœ… Best practices & common pitfalls

Let’s dive in. πŸ”₯


βš™οΈ What is CSP & Why Do We Need It?

  • Content Security Policy (CSP) is an HTTP response header that defines where browsers can load resources (scripts, images, styles, fonts, iframes, workers, etc.) from.
  • Its main purpose: prevent XSS attacks by restricting execution of malicious scripts.
  • Without CSP, if an attacker injects a script into your page, the browser executes it blindly. With CSP, you can whitelist only trusted domains, blocking everything else.

Example CSP header:

Content-Security-Policy: default-src 'self'; img-src https://cdn.example.com; script-src 'self' https://apis.google.com;
Enter fullscreen mode Exit fullscreen mode

This means:

  • By default, only load resources from the same origin ('self').
  • Allow images from cdn.example.com.
  • Allow scripts from self and Google APIs.

⚠️ Important:
If default-src is missing, everything is allowed, weakening your CSP.


πŸ“Œ Important Directives

Here’s a breakdown of commonly used CSP directives:

πŸ”‘ default-src

The fallback for all other directives (if they are not explicitly defined).

Content-Security-Policy: default-src 'self';
Enter fullscreen mode Exit fullscreen mode

If script-src or style-src is missing, browsers fall back to default-src. βœ…


πŸ“œ script-src

Defines where JavaScript can be loaded from.

Content-Security-Policy: script-src 'self' https://apis.google.com;
Enter fullscreen mode Exit fullscreen mode
  • Special values:

    • 'self': allow only current domain
    • 'unsafe-eval': allow eval() usage (⚠️ dangerous)
    • 'unsafe-inline': allow inline scripts (⚠️ not recommended)
  • Better options:

    • Nonce β†’ Random value per request:
    <script nonce="abc123">console.log("Safe!");</script>
    

    With CSP:

    Content-Security-Policy: script-src 'self' 'nonce-abc123';
    
    • Hash β†’ Allow only exact inline script:
    <script>alert("hello")</script>
    

    With CSP:

    Content-Security-Policy: script-src 'self' 'sha256-XYZhashHere...';
    

βœ… Nonce & Hash are safer than 'unsafe-inline'.


🎨 style-src

Controls where CSS can be loaded from.

Content-Security-Policy: style-src 'self' https://fonts.googleapis.com;
Enter fullscreen mode Exit fullscreen mode

⚠️ Using 'unsafe-inline' allows inline styles β†’ risky.
πŸ”‘ Instead:

  • Use nonce or hashes for dynamic styles.
  • Example:
  <style nonce="abc123">.btn { color: red; }</style>
Enter fullscreen mode Exit fullscreen mode

πŸ–ΌοΈ img-src

Defines allowed image sources.

Content-Security-Policy: img-src 'self' https://cdn.example.com data:;
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ data: allows base64-encoded images.


πŸ“„ font-src

Controls allowed font sources.

Content-Security-Policy: font-src 'self' https://fonts.gstatic.com;
Enter fullscreen mode Exit fullscreen mode

πŸ”— connect-src

Defines where AJAX/fetch/WebSocket connections can be made.

Content-Security-Policy: connect-src 'self' https://api.example.com;
Enter fullscreen mode Exit fullscreen mode

🎡 media-src

Specifies allowed sources for <audio> and <video>.

Content-Security-Policy: media-src 'self' https://media.example.com;
Enter fullscreen mode Exit fullscreen mode

πŸ—οΈ frame-src vs frame-ancestors

  • frame-src: Controls what your app can embed in an <iframe>.
  Content-Security-Policy: frame-src https://www.youtube.com;
Enter fullscreen mode Exit fullscreen mode
  • frame-ancestors: Controls who can embed your site.
  Content-Security-Policy: frame-ancestors 'none';
Enter fullscreen mode Exit fullscreen mode

βœ… Use frame-ancestors to prevent clickjacking.


🧩 worker-src

Defines allowed sources for Web Workers & Service Workers.

  • Web Workers: Run scripts in a separate thread (background tasks).
  • Service Workers: Proxy requests for offline support / caching.
Content-Security-Policy: worker-src 'self' https://cdn.example.com;
Enter fullscreen mode Exit fullscreen mode

πŸ“‚ object-src

Restricts plugins like Flash/Silverlight (legacy). Best to set 'none'.

Content-Security-Policy: object-src 'none';
Enter fullscreen mode Exit fullscreen mode

πŸ“ Reporting with CSP

Instead of enforcing, you can test with reporting only:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint;
Enter fullscreen mode Exit fullscreen mode

This way, violations are logged but not blocked. Useful before rollout.


πŸ’‘ Real-World Example

Imagine your webapp loads:

  • Scripts from your domain + Google APIs
  • Styles from your domain + Google Fonts
  • Images from CDN

CSP could look like:

Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.google.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' https://cdn.example.com; connect-src 'self' https://api.myapp.com; object-src 'none'; frame-ancestors 'none'
Enter fullscreen mode Exit fullscreen mode

This protects you from attackers injecting malicious scripts/images from untrusted domains.


🚨 Common CSP Issues Developers Face

  • ❌ Images not loading β†’ Forgot img-src domain.
  • ❌ Fonts breaking β†’ Missing font-src.
  • ❌ Videos not playing β†’ media-src missing.
  • ❌ API calls failing β†’ Missing connect-src.
  • ❌ App not embeddable β†’ Strict frame-ancestors.

πŸ‘‰ If your frontend/backend look fine but still break, check CSP first.


βœ… Best Practices

  • Always define a strict default-src.
  • Avoid 'unsafe-inline' and 'unsafe-eval' unless absolutely necessary – use nonces or hashes.
  • Use 'self' wherever possible.
  • Test with Content-Security-Policy-Report-Only before enforcing.

πŸ“Œ Conclusion

CSP is not just another headerβ€”it’s a powerful security shield πŸ›‘οΈ for your apps.
The right configuration prevents XSS, clickjacking, and data injection while keeping performance intact.

πŸ‘‰ The bottom line: CSP = Control + Security + Peace of Mind.

Top comments (0)