Add a Content-Security-Policy
Web headers · about 1 hour
In plain words
A CSP is an allow-list of where your pages may load scripts, styles and images from. If an attacker manages to inject a script, the browser refuses to run it.
What can go wrong
Without a CSP (or with 'unsafe-inline' / 'unsafe-eval' / wildcards), a single injection bug becomes account takeover, card skimming or defacement.
How to fix it
Pick the platform your site runs on. Not sure? Forward this page to whoever manages your website.
Nginx
# inside the server { } block
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" always;Apache / .htaccess
<IfModule mod_headers.c>
Header always set Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"
</IfModule>Cloudflare
Rules → Transform Rules → Modify Response Header
Set static: Content-Security-Policy-Report-Only = default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'
# Cloudflare Pages: add to public/_headers
/*
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'Vercel
// vercel.json
{ "headers": [ { "source": "/(.*)", "headers": [
{ "key": "Content-Security-Policy-Report-Only", "value": "default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'" }
] } ] }Netlify
# public/_headers
/*
Content-Security-Policy-Report-Only: default-src 'self'; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'WordPress
// functions.php (child theme) — or use your security plugin's "HTTP headers" setting
add_action('send_headers', function () {
header('Content-Security-Policy-Report-Only: default-src \'self\'; img-src \'self\' data:; script-src \'self\'; style-src \'self\' \'unsafe-inline\'; frame-ancestors \'none\'; base-uri \'self\'; form-action \'self\'');
});Before you change anything: Start with Content-Security-Policy-Report-Only (shown above) and watch the browser console for a few days. Add the domains your site really needs (analytics, payment, fonts), then rename the header to Content-Security-Policy. For scripts, prefer nonces or hashes over 'unsafe-inline'.
Check that it worked
Re-run the ProbeHunt check — it flags a missing CSP and permissive script-src values.