Skip to content
← All articles

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

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 →
More articles: SEC
SEC
Prevent XSS Attacks: Escaping, CSP and Trusted Types
15.04.2026 · 4 views
SEC
Clickjacking Prevention: X-Frame-Options vs frame-ancestors
15.04.2026 · 6 views
SEC
Subresource Integrity (SRI): Protecting CDN Scripts
15.04.2026 · 5 views
SEC
WAF (Web Application Firewall): A Practical Guide
15.04.2026 · 5 views