How to: Implement a Content Security Policy (CSP)

How to: Implement a Content Security Policy (CSP)

When to use this guide

  • Content-Security-Policy header not implemented
  • CSP score is -25 or lower
How urgent is this? CSP is medium priority but technically complex. A missing CSP leaves the site vulnerable to cross-site scripting (XSS) attacks — where an attacker injects malicious scripts into your pages. However, a misconfigured CSP can break the site entirely (blocking legitimate scripts, payment widgets, fonts, or analytics). Do not rush this. Take time to test thoroughly in report-only mode before enforcing.
This guide requires developer access. Implementing CSP correctly requires inspecting which scripts, stylesheets, fonts, and other resources the site loads, and where they come from. On WordPress this typically means access to the theme or a code-level plugin. This is not a one-click fix.

Who does this?

The developer responsible for the WordPress theme or codebase. Basic WordPress admin access is not sufficient — this requires someone comfortable reading browser console output and editing code or advanced plugin settings.


Background: what does CSP do?

Concept
What it does
Content-Security-Policy
Tells the browser which sources are allowed to load scripts, styles, images, and other resources
XSS protection
Prevents attackers from injecting and executing malicious scripts on your pages
report-only mode
Logs violations without blocking anything — safe to use while building the policy
enforce mode
Actively blocks disallowed resources — only use after thorough testing


Step 1: Audit what your site loads

Before writing a CSP, you need to know what resources your site loads and from where. Open the site in Chrome or Firefox, open DevTools (F12) → Network tab, reload the page, and note:
  • All script sources ( <script> tags and JS files)
  • All stylesheet sources ( <link> tags and CSS files)
  • All font sources (Google Fonts, Adobe Fonts, etc.)
  • All iframe sources (YouTube embeds, payment widgets, etc.)
  • All image sources
Pay special attention to third-party sources (Google Analytics, Facebook Pixel, payment providers, CDNs).


Step 2: Start with report-only mode

Add the following HTTP header via your WordPress security plugin (e.g. HTTP Headers by Fer Perez):
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
This logs all violations to the browser console without blocking anything. Leave it running for a few days and collect violations — they will tell you exactly which sources need to be whitelisted.


Step 3: Build the policy

Based on the violations collected in Step 2, build a policy that allows all legitimate sources. A typical WordPress site using Google Fonts and Analytics might look like:
Content-Security-Policy:
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
frame-src 'self' https://www.youtube.com;
Avoid unsafe-inline and unsafe-eval where possible — they significantly weaken the policy. Some WordPress themes and plugins require them, but try to minimise their use.


Step 4: Switch to enforce mode

Once the policy in report-only mode produces no unexpected violations:
    Replace Content-Security-Policy-Report-Only with Content-Security-Policy
    Test all key pages: homepage, donation flow, contact form, any embedded widgets
    Check the browser console for any new violations and fix them


Step 5: Verification

  • Re-run  securityheaders.com  — CSP should now show green
  • Re-run  Mozilla HTTP Observatory  — score should improve significantly
  • Check the browser console on key pages for any remaining CSP violations