Build a Content Security Policy header for your website.
Content Security Policy (CSP) is a security standard that helps prevent cross-site scripting (XSS), clickjacking, and other code injection attacks. By specifying which content sources are legitimate, CSP instructs browsers to only execute or render resources from those approved sources.
CSP acts as a whitelist mechanism—if a resource isn't explicitly allowed by your policy, the browser blocks it. This provides a powerful defense-in-depth layer even if an attacker manages to inject malicious code into your pages.
| Directive | Controls | Example |
|---|---|---|
default-src |
Fallback for all other directives | 'self' |
script-src |
JavaScript sources | 'self' https://cdn.example.com |
style-src |
CSS sources | 'self' 'unsafe-inline' |
img-src |
Image sources | 'self' data: https: |
font-src |
Font file sources | 'self' https://fonts.gstatic.com |
connect-src |
AJAX, WebSocket, EventSource | 'self' https://api.example.com |
frame-src |
iframe sources | 'self' https://youtube.com |
object-src |
Plugins (Flash, Java) | 'none' |
base-uri |
URLs for <base> element | 'self' |
form-action |
Form submission URLs | 'self' |
frame-ancestors |
Who can embed this page | 'none' |
| Value | Meaning |
|---|---|
'self' |
Same origin only (same protocol, domain, and port) |
'none' |
Block all sources |
'unsafe-inline' |
Allow inline scripts/styles (reduces security) |
'unsafe-eval' |
Allow eval() and similar (reduces security) |
'strict-dynamic' |
Trust scripts loaded by already-trusted scripts |
https: |
Any HTTPS URL |
data: |
Data URIs (e.g., base64 images) |
blob: |
Blob URIs |
'nonce-xyz' |
Allow specific inline script with matching nonce |
'sha256-...' |
Allow script/style matching hash |
Use Content-Security-Policy-Report-Only header to test your policy without breaking your site:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-reports
This logs violations without blocking them, helping you refine your policy.
Instead of 'unsafe-inline', generate a random nonce for each request:
<script nonce="random123">...</script>
Content-Security-Policy: script-src 'nonce-random123'
Calculate the SHA-256 hash of your inline script content:
Content-Security-Policy: script-src 'sha256-abc123...'
Content-Security-Policy:
default-src 'self';
script-src 'self' 'strict-dynamic' 'nonce-{random}';
style-src 'self';
img-src 'self' data: https:;
font-src 'self';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
img-src 'self' https://www.google-analytics.com;
connect-src 'self' https://www.google-analytics.com;
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com;
style-src 'self' https://cdn.jsdelivr.net 'unsafe-inline';
font-src 'self' https://fonts.gstatic.com;
Adding third-party services requires updating your CSP. Here are common additions:
font-src https://fonts.gstatic.com; style-src https://fonts.googleapis.comframe-src https://www.youtube.comscript-src https://js.stripe.com; frame-src https://js.stripe.comscript-src https://www.google.com https://www.gstatic.com; frame-src https://www.google.comAvoid if possible:
'unsafe-inline' - Allows XSS'unsafe-eval' - Allows code injection* (wildcard) - Allows any sourceThese defeat the purpose of CSP. Use nonces or hashes instead.
object-src 'none'frame-ancestors instead of X-Frame-Options