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;
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';
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;
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;
β οΈ Using 'unsafe-inline' allows inline styles β risky.
π Instead:
πΌοΈ img-src
Defines allowed image sources.
Content-Security-Policy: img-src 'self' https://cdn.example.com data:;
π data: allows base64-encoded images.
π font-src
Controls allowed font sources.
Content-Security-Policy: font-src 'self' https://fonts.gstatic.com;
π connect-src
Defines where AJAX/fetch/WebSocket connections can be made.
Content-Security-Policy: connect-src 'self' https://api.example.com;
Specifies allowed sources for <audio> and <video>.
Content-Security-Policy: media-src 'self' https://media.example.com;
ποΈ 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;
frame-ancestors: Controls who can embed your site.
Content-Security-Policy: frame-ancestors 'none';
β
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;
π object-src
Restricts plugins like Flash/Silverlight (legacy). Best to set 'none'.
Content-Security-Policy: object-src 'none';
π 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;
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'
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.