Content Security Policy

Content Security Policy (CSP) is a powerful security feature delivered via HTTP response headers that helps protect your WordPress site from Cross-Site Scripting (XSS) attacks, data injection attacks, and other code execution vulnerabilities. By defining approved sources of content that browsers may load, CSP acts as an additional security layer that can significantly reduce the risk of malicious code execution on your site.

Why WordPress Sites Need CSP

When a visitor loads your WordPress site, their browser loads numerous assets: stylesheets, JavaScript files, images, fonts, and content from various plugins and themes. Without CSP, the browser has no way to distinguish between legitimate resources and malicious ones. If an attacker successfully injects malicious code through a vulnerable plugin, theme, or comment system, the browser will execute it without question.

WordPress sites are particularly vulnerable because they often rely on multiple plugins and themes from different developers, each potentially loading resources from various sources. A single compromised plugin could inject malicious JavaScript that steals user credentials, redirects visitors to phishing sites, or performs unauthorized actions.

How Content Security Policy Works

CSP works by whitelisting approved sources for different types of content. When a browser receives a CSP header, it only loads resources from the specified sources and blocks everything else. This means even if an attacker manages to inject malicious code into your site, the browser will refuse to execute it if it doesn’t come from an approved source.

A basic CSP header looks like this:

Content-Security-Policy: script-src 'self'

This policy tells the browser to only load JavaScript from your own domain. Any scripts from external domains will be blocked, effectively neutralizing many XSS attacks.

CSP Directives for WordPress

CSP provides multiple directives to control different types of content. Here are the most important ones for WordPress sites:

  • default-src – Fallback policy for all resource types not explicitly defined
  • script-src – Controls which JavaScript sources are allowed to execute
  • style-src – Defines approved sources for CSS stylesheets
  • img-src – Specifies allowed sources for images
  • font-src – Controls where fonts can be loaded from
  • connect-src – Defines allowed URLs for AJAX, WebSocket, and EventSource connections
  • frame-src – Controls which URLs can be embedded in frames or iframes
  • media-src – Specifies sources for video and audio elements
  • object-src – Controls plugins like Flash (should typically be set to ‘none’)
  • form-action – Restricts URLs that can be used as form submission targets
  • report-uri – Specifies where violation reports should be sent

Building a CSP Policy for WordPress

Creating an effective CSP policy for WordPress requires understanding what resources your site loads. Here are example policies with increasing levels of security:

Basic HTTPS-only policy:
Content-Security-Policy: default-src https:
This allows any resource over HTTPS, preventing mixed content issues but offering limited XSS protection.

Restrictive policy with common CDNs:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;
This allows scripts from your domain and specific CDNs, inline styles (common in WordPress), images from anywhere over HTTPS, and fonts from your domain.

Strict policy for maximum security:
Content-Security-Policy: default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';
This blocks everything by default and only allows resources from your own domain.

CSP Keywords and Wildcards

CSP supports several special keywords and wildcard patterns:

  • ‘none’ – Blocks all resources of this type
  • ‘self’ – Allows resources from your current domain (excluding subdomains)
  • ‘unsafe-inline’ – Permits inline JavaScript and CSS (reduces security)
  • ‘unsafe-eval’ – Allows JavaScript eval() and similar functions (reduces security)
  • Wildcards – Use patterns like *.example.com to match all subdomains

You can specify schemes, domains, and ports with varying specificity:

  • https://example.com – Only this exact domain over HTTPS
  • https://example.com:443 – Specific port required
  • *.example.com – All subdomains (but not the root domain)
  • https: – Any HTTPS source

Testing Your CSP Policy

Before enforcing a CSP policy on your live WordPress site, you should test it thoroughly using the Content-Security-Policy-Report-Only header. This special header tells browsers to report violations without actually blocking content, allowing you to identify issues before they affect your visitors.

To implement report-only mode, use:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri https://yoursite.com/csp-report

You can even send both headers simultaneously – one to enforce your current policy and another to test changes:

Content-Security-Policy: default-src 'self';
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://newcdn.com;

This approach lets you safely test policy modifications without risking site functionality.

CSP Violation Reporting

The report-uri directive enables automatic violation reporting. When a browser blocks content due to your CSP policy, it can send a JSON report to your specified endpoint:

Content-Security-Policy: default-src 'self'; report-uri https://yoursite.com/csp-violations

A typical violation report contains:

  • document-uri – The page where the violation occurred
  • blocked-uri – The resource that was blocked
  • violated-directive – Which CSP directive was violated
  • original-policy – Your complete CSP policy

Security Warning: Violation reports should be handled carefully. Never trust the content of these reports, as attackers can craft malicious reports. Don’t automatically visit URLs in reports, and implement rate limiting to prevent DDoS attacks on your reporting endpoint.

Common WordPress CSP Challenges

Implementing CSP on WordPress sites presents unique challenges:

Inline Scripts and Styles

Many WordPress plugins and themes use inline JavaScript and CSS, which CSP blocks by default for security reasons. While you can use 'unsafe-inline' to allow them, this significantly weakens your security posture.

Better alternatives include:

  • Nonces – Generate a random value for each page load and include it in both the CSP header and script tags: script-src 'nonce-RandomValue123' and
  • Hashes – Include a hash of the script content in your policy: script-src 'sha256-HashOfScriptContent='
  • Externalizing scripts – Move inline code to external .js files

Third-Party Services

WordPress sites commonly use external services like Google Analytics, Google Fonts, CDNs, social media widgets, and advertising networks. Each requires specific CSP permissions:

Google Analytics:
script-src https://www.google-analytics.com; img-src https://www.google-analytics.com;

Google Fonts:
font-src https://fonts.gstatic.com; style-src https://fonts.googleapis.com;

YouTube embeds:
frame-src https://www.youtube.com;

WordPress Admin Area

The WordPress admin dashboard uses inline scripts extensively. You may need a separate, more permissive CSP policy for /wp-admin/ URLs while maintaining strict policies for your public-facing pages.

Implementing CSP in WordPress

There are several methods to add CSP headers to your WordPress site:

Using .htaccess (Apache)

Add to your .htaccess file:

Header set Content-Security-Policy "default-src 'self';"

Using wp-config.php

Add before the “stop editing” comment:

header("Content-Security-Policy: default-src 'self';");

Using functions.php

Add to your theme’s functions.php:

add_action('send_headers', 'add_csp_header');
function add_csp_header() {
header("Content-Security-Policy: default-src 'self';");
}

Using Security Plugins

WP Security Ninja and other security plugins provide user-friendly interfaces for configuring CSP headers without editing code, making it easier to manage and update your policies.

CSP Best Practices for WordPress

  1. Start with Report-Only mode – Always test policies before enforcement
  2. Begin restrictive, then relax – Start with default-src 'none' and add permissions as needed
  3. Avoid ‘unsafe-inline’ and ‘unsafe-eval’ – These significantly reduce CSP effectiveness
  4. Use ‘self’ instead of your domain – It’s more maintainable and works across environments
  5. Set object-src to ‘none’ – Plugins like Flash are security risks
  6. Monitor violation reports – They reveal both attacks and policy issues
  7. Document your policy – Note why each source is whitelisted
  8. Review regularly – Update your policy when adding/removing plugins
  9. Test across browsers – CSP support varies slightly between browsers
  10. Combine with other security measures – CSP is one layer in defense-in-depth

Browser Support and Compatibility

Modern browsers widely support CSP, including Chrome, Firefox, Safari, Edge, and Opera. However, older versions of Internet Explorer (IE 10 and below) require the X-Content-Security-Policy header instead. For maximum compatibility, you may need to send both headers, though IE 11 and Edge support the standard header.

Important: CSP is enforced on a per-page basis. Browsers don’t cache CSP policies, so you must send the header with every response you want protected.

Common CSP Policy Mistakes

Avoid these frequent errors when implementing CSP:

  • Duplicate directivesscript-src 'self'; script-src https://cdn.com; will ignore the second directive. Combine them: script-src 'self' https://cdn.com;
  • Assuming inheritance – Specific directives don’t inherit from default-src. If you set default-src https: and script-src 'self', scripts won’t load over HTTPS from your domain unless you specify it
  • Overly permissive wildcards – Using https://* defeats the purpose of CSP
  • Forgetting data: URIs – Many WordPress themes use data URIs for images; include data: in img-src
  • Not testing thoroughly – Test all site functionality, including forms, AJAX requests, and admin features

Advanced CSP Features

Frame Ancestors

Prevent your site from being embedded in iframes (clickjacking protection):

frame-ancestors 'none';

Upgrade Insecure Requests

Automatically upgrade HTTP requests to HTTPS:

upgrade-insecure-requests;

Sandbox

Apply restrictions similar to iframe sandbox attribute:

sandbox allow-forms allow-scripts;

Real-World WordPress CSP Example

Here’s a practical CSP policy for a typical WordPress site using common services:

Content-Security-Policy: default-src 'self'; script-src 'self' https://www.google-analytics.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https: ; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-src https://www.youtube.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests; report-uri https://yoursite.com/csp-report;

This policy:

  • Allows resources from your domain by default
  • Permits scripts from Google Analytics and jsDelivr CDN
  • Allows inline styles (common in WordPress) and Google Fonts
  • Permits images from anywhere over HTTPS and data URIs
  • Allows fonts from your domain and Google Fonts
  • Restricts AJAX to your domain
  • Permits YouTube embeds
  • Blocks plugins like Flash
  • Prevents clickjacking
  • Upgrades HTTP to HTTPS
  • Reports violations

Troubleshooting CSP Issues

If your WordPress site breaks after implementing CSP:

  1. Check browser console – CSP violations appear in developer tools
  2. Review violation reports – Identify which resources are being blocked
  3. Switch to Report-Only mode – Temporarily stop enforcement while debugging
  4. Add sources incrementally – Whitelist one source at a time to identify conflicts
  5. Test with plugins disabled – Identify which plugin requires specific permissions
  6. Check for inline scripts – Look for onclick attributes and inline event handlers

CSP and WordPress Performance

While CSP primarily enhances security, it can also improve performance by:

  • Preventing unwanted third-party scripts from loading
  • Blocking malicious cryptocurrency miners
  • Reducing bandwidth from blocked resources
  • Encouraging best practices like externalizing scripts

Further Resources

For a deeper dive into Content Security Policy, see Scott Helme’s comprehensive CSP introduction.

Conclusion

Content Security Policy is a powerful tool for protecting WordPress sites from XSS attacks and code injection vulnerabilities. While implementing CSP requires careful planning and testing, especially given WordPress’s plugin ecosystem and reliance on third-party services, the security benefits are substantial. Start with a report-only policy, gradually refine your whitelist, and monitor violation reports to build an effective CSP that protects your site without breaking functionality. Combined with other security measures like regular updates, strong authentication, and comprehensive security plugins like WP Security Ninja, CSP forms an essential part of a defense-in-depth security strategy.

Was this helpful?