Content-Security-Policy without breaking your site
A CSP tells the browser which sources of scripts, styles and images to trust. It is your best defence against injected scripts and card skimmers, and it rewards a careful rollout.
1. Start in report-only mode
The browser reports violations in the console but blocks nothing.
add_header Content-Security-Policy-Report-Only "default-src 'self'; img-src 'self' data: https:; script-src 'self'; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; object-src 'none'; base-uri 'self'" always;2. Browse your site and read the console
Open DevTools → Console on your key pages (home, a product, the cart and checkout, the contact form). Every blocked source shows up there. Add the ones you trust, such as https://www.googletagmanager.com or your payment provider, to the matching directive.
3. Enforce
When the console is quiet, rename the header to Content-Security-Policy. Keep the report-only header alongside it while you test a stricter version.
Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data: https:; script-src 'self' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline'; frame-ancestors 'self'; object-src 'none'; base-uri 'self'"WordPress tips
- Many themes and plugins use inline scripts. You may need
'unsafe-inline'inscript-srcat first. It’s weaker, butframe-ancestors,object-srcandbase-uristill help. - For a strict policy, use nonces: WordPress 5.7+ can add attributes to scripts printed with
wp_print_inline_script_tag(). - Put a strict CSP on your checkout first, where it matters most.
Useful directives
frame-ancestors 'self': modern replacement for X-Frame-Options.upgrade-insecure-requests: auto-upgrades http:// resources to https://.connect-src: limits where scripts can send data, which is useful against skimmers.