Browse all patterns

How to: Implement a Content Security Policy (CSP)

When to use this guide

Use this guide when securityheaders.com or Mozilla HTTP Observatory flags:
  • 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?

ConceptWhat it does
Content-Security-PolicyTells the browser which sources are allowed to load scripts, styles, images, and other resources
XSS protectionPrevents attackers from injecting and executing malicious scripts on your pages
report-only modeLogs violations without blocking anything โ€” safe to use while building the policy
enforce modeActively 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:
  1. Replace Content-Security-Policy-Report-Only with Content-Security-Policy
  2. Test all key pages: homepage, donation flow, contact form, any embedded widgets
  3. Check the browser console for any new violations and fix them

Step 5: Verification