CSP (Content Security Policy): Setup Guide
CSP (Content Security Policy): Setup Guide from Scratch
Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of scripts, styles, images, fonts and other assets are legitimate. A properly authored CSP turns an XSS vulnerability into an incident log entry instead of a full session takeover. This guide covers the entire rollout — from Report-Only to strict strict-dynamic with nonces.
How CSP works
The browser reads the Content-Security-Policy header and, for every fetch directive (script-src, style-src, img-src, font-src, etc.), checks whether the origin is permitted. Disallowed resources are blocked and, if report-uri or report-to is set, a JSON CSP report is sent. The spec lives at W3C CSP Level 3.
Core directives
default-src is the fallback for every fetch directive; script-src gates scripts; style-src — stylesheets; img-src — images; connect-src — fetch/XHR/WebSocket; font-src — fonts; frame-src — iframes; frame-ancestors — the modern X-Frame-Options; object-src — plugin objects (use 'none'); base-uri — the <base> element (use 'self'); form-action — form targets.
Source expressions: 'self', nonce, hash, strict-dynamic
'self'— the current origin'nonce-RANDOM'— one-shot random value per request; scripts carryingnonce="RANDOM"are allowed'sha256-HASH'— allows the inline script matching this SHA-256 hash'strict-dynamic'— a nonce/hash-trusted script can load additional scripts; simplifies bundler output'unsafe-inline','unsafe-eval'— avoid in production
Example of a strict policy
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{NONCE}' 'strict-dynamic';
style-src 'self' 'nonce-{NONCE}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests
A nonce must be 128 bits of cryptographically random data generated per request. Reusing one nonce across an SPA template is equivalent to 'unsafe-inline'.
Report-Only mode for debugging
Before enforcing, run the policy in report-only:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report; report-to csp-endpoint
Collect reports for 2–4 weeks, whitelist legitimate sources, drop dead ones, and only flip to enforce once the logs go quiet.
nginx + nonce (with ngx_http_sub_module)
set_secure_random_alphanum $cspNonce 32;
sub_filter_once off;
sub_filter 'NONCE_PLACEHOLDER' $cspNonce;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$cspNonce' 'strict-dynamic'; style-src 'self' 'nonce-$cspNonce'; object-src 'none'; base-uri 'self'" always;
Next.js 15 middleware
import { NextResponse } from 'next/server';
export function middleware(request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; style-src 'self' 'nonce-${nonce}'; object-src 'none'; base-uri 'self'`;
const headers = new Headers(request.headers);
headers.set('x-nonce', nonce);
const res = NextResponse.next({ request: { headers } });
res.headers.set('Content-Security-Policy', csp);
return res;
}
Verification and reporting
Check the policy: curl -I https://example.com | grep -i content-security or use the enterno.io CSP Analyzer — it parses directives, flags missing ones and assigns an A–F grade. Related headers live in HTTP Security Headers.
FAQ
Google Analytics broke — why? Add https://www.googletagmanager.com to script-src and https://www.google-analytics.com to connect-src.
What about inline style="..."? CSP3 supports 'unsafe-hashes' for attributes, but externalising the CSS is always better.
strict-dynamic instead of hosts? Yes — it ignores the script host-allowlist and trusts only nonces/hashes, which makes long-term maintenance painless.
Can CSP ride in a <meta> tag? Technically yes, but frame-ancestors and report-uri are ignored. Fallback only.
Conclusion
CSP rollout strategy: 1) audit resources, 2) enable Report-Only, 3) after 2–4 weeks switch to enforce, 4) add monitoring on header changes, 5) validate with the CSP Analyzer. See also: XSS protection, SRI for CDN.
Check your website right now
Check now →