Content-Security-Policy (CSP) is the highest-impact header against XSS — and the easiest to break
your own site with. The trick: start in report-only mode, see what it would block, then enforce.
Step 1 — report-only (breaks nothing)
This header logs violations but does NOT block anything. Ship it first:
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Open your site, watch the browser console for “would have been blocked” messages, and add the sources you actually need.
Step 2 — enforce
Once report-only is clean, rename the header to enforce it:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Per server (enforcing form)
Nginx:
add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'" always;
Apache:
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
Caddy:
header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
_headers:
/*
Content-Security-Policy: default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'
Inline scripts/styles
'self' blocks inline <script> and style="". If you can’t remove them, prefer a nonce or
hash over 'unsafe-inline':
script-src 'self' 'nonce-RANDOM_PER_REQUEST'
Generate a fresh random nonce on each response and put the same value in the tag’s nonce
attribute. Avoid 'unsafe-inline' for script-src — it defeats most of CSP’s XSS protection.
Verify
curl -sI https://yourdomain.com | grep -i content-security-policy
Common additions: font-src 'self' data:, connect-src 'self' https://api.example.com (for
fetch/XHR/WebSocket), frame-src for embeds. Add only what your site actually loads, and keep
object-src 'none' and base-uri 'self' — they’re cheap, high-value defenses.