This content originally appeared on DEV Community and was authored by Fazal Mansuri
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.
This content originally appeared on DEV Community and was authored by Fazal Mansuri