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:
Ifdefault-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'
: alloweval()
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:
- Use nonce or hashes for dynamic styles.
- Example:
<style nonce="abc123">.btn { color: red; }</style>
πΌοΈ 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;
π΅ media-src
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.
Top comments (0)