DevelopersJetpack 101

How to Force Content Security Policy (CSP) Headers on WordPress

man wearing headphones working on a laptop

Content Security Policy (CSP) is a browser feature that blocks unsafe content. It tells the browser what resources it can load, which can prevent attacks like cross‑site scripting (XSS). It also helps stop mixed content issues, which occur when secure pages load insecure files.

While CSP sounds complicated, you don’t need advanced technical experience to use it — just a simple starting policy, a safe way to test, and a little fine-tuning. This guide explains how to plan a policy, test it safely, enforce it, and maintain it over time. It includes a beginner path using a plugin and a developer path using server or code-level headers.

Who this guide is for and how to use it

This guide supports two experience levels:

  • The beginner track: You have WordPress admin access but don’t want to edit server config. You’ll use a plugin to add CSP and follow a clear test loop.
  • The developer track: You can edit Apache, Nginx, CDN headers, or WordPress code. You want direct control, versioning, and support for nonces or route-based policies.

You can read the shared foundation first, then jump to your path.

CSP basics before you get started

To deploy a solid policy, you need to understand a few terms:

  • Directive: This is a rule for one resource type. Example: script-src controls JavaScript.
  • Source expression: This is a value inside a directive. Example: ‘self’ or https://cdn.example.com.
  • ‘self’. This allows resources from your own domain and subresources served from it.
  • Report-only mode: The browser records violations but still loads blocked resources.
  • Enforcement mode: The browser blocks violations instead of only logging them.
  • Nonce: A random token that allowlists one inline script or style for one request.
  • Hash: An SHA-based fingerprint that allowlists one exact inline script or style text.

A CSP header is a semicolon separated list of directives. Browsers apply the rules to every page response that includes the header.

Before you set up CSP, make sure that:

  • You can clear all caches. This includes caching through plugins, your host, or a CDN.
  • You can test on staging or during a low traffic window. CSP can break your site layout or code if done incorrectly.
  • You know which third-party services your site uses. This includes things like analytics, fonts, ads, embeds, payment gateways, and chat widgets.

Decide on policy rules

When choosing which resources to allow, consider:

  • Scripts (script-src): From things like plugins, analytics, and inline code
  • Styles (style-src): Like theme CSS and external fonts
  • Images (img-src): From your Media Library and external image hosts
  • Connections (connect-src): From the REST API and external APIs
  • Other resources: Like fonts, frames, etc.

Start with a simple policy. For example:

default‑src 'self';
script‑src 'self' https://www.google‑analytics.com;
style‑src 'self' 'unsafe‑inline';
img‑src 'self' data:;

This allows files from your site, Google Analytics scripts, inline styles, and inline images. You can expand later.

At this point, it’s time to choose your track. If you’re a beginner, read on to the next line. If you’re more experienced, skip down to the developer track.

Beginner track: Add CSP with a plugin

Step 1: Install and configure a CSP plugin

We’ll use the Headers Security Advanced & HSTS WP plugin here, but any plugin that allows you to set custom response headers works.

  1. Install the plugin from the WordPress plugin directory.
  2. Go to Settings → Headers Security Advanced & HSTS WP.
  3. Find the CSP Header Contents section and paste your policy. Use the “starter policy template” section below if you need help writing a policy.
  4. Add a CSP Report URI if you’re in report-only mode (recommended). You can use:
    • A service such as Report URI or Sentry CSP reporting.
    • Your own endpoint if you already log security reports.
  5. Save your settings.
  6. Clear all caches.
CSP header content and report URI settings

Starter policy templates

Pick one that matches your setup, then refine it.

Standard template:

• default-src 'self';
• script-src 'self';
• style-src 'self' 'unsafe-inline';
• img-src 'self' data:;
• font-src 'self';
• connect-src 'self';

Typical WordPress setup with Google Fonts and Google Analytics:

• default-src 'self';
• script-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
• style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
• font-src 'self' https://fonts.gstatic.com data:;
• img-src 'self' data:;
• connect-src 'self';

WordPress with YouTube embeds:

• default-src 'self';
• script-src 'self';
• style-src 'self' 'unsafe-inline';
• img-src 'self' data: https://i.ytimg.com;
• frame-src https://www.youtube.com https://www.youtube-nocookie.com;

Step 2: Check reports

After enabling report-only mode, browse your site and watch for reports. You can do this with tools like Chrome DevTools, keeping an eye out for CSP warnings.

A typical report contains keys such as:

  • blocked-uri: The resource the browser would block in enforcement mode.
  • violated-directive: The rule that would block it.
  • source-file: The page where the violation happened.

Your job is simple:

  1. Decide if the blocked resource is necessary and trusted.
  2. If it is, add its domain to the matching directive.
  3. Save, clear your cache, and reload.

Repeat until your critical pages generate no important violations.

Step 3: Move to enforcement mode

Once reports are clean, switch to enforcement mode.

  1. Replace the report-only header with an enforced CSP header.
  2. Paste the exact same policy.
  3. Save and clear caches.
  4. Reload key pages and confirm that no critical resources are blocked.

You’ll still see violations, but now the browser will block what breaks the policy.

Developer track method 1: Add CSP at the server level

Are you a developer who wants more control? There are two primary ways to add a CSP to WordPress.

Apache, using .htaccess 

In .htaccess add the following, customizing as needed:

<IfModule mod_headers.c>
  Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
</IfModule>

After testing, switch to enforcement:

<IfModule mod_headers.c>
  Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
</IfModule>

Nginx

In your server block add the following, customized for your situation:

add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;

Then enforce:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;

The always keyword makes sure headers are sent even on error responses.

CDN or reverse proxy

If your CSP should apply across multiple origins or you want a single control point:

  • Add the CSP header in your CDN response header rules.
  • Keep the policy string in version control if your CDN supports config as code.
  • Avoid setting CSP both at origin and CDN unless you intend to replace the origin value.

Developer track method 2: Add CSP via WordPress code

You may want CSP inside WordPress when your setup requires dynamic behavior. For example:

  • You need per-page policies, such as a tighter rule on admin-facing pages.
  • You plan to add nonces for inline scripts you control.
  • You want CSP rollout tied to theme or plugin deployment.

Example using wp_headers:

<IfModule mod_headers.c>
  Header set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
</IfModule>

After testing, change the header key to Content-Security-Policy.

Advanced CSP in WordPress: Nonces and hashes

Inline scripts are common in WordPress, as page builders, themes, and plugins may inject them. There are three ways to handle them:

  • Allow inline scripts globally with ‘unsafe-inline’. This is the easiest method, but isn’t as strong as the others.
  • Add hashes for specific inline blocks. This is stable when the content doesn’t change often.
  • Add nonces per request. This method is strong and flexible, but you must inject the nonce into every inline block you want to allow.

Suppose your theme outputs this inline script:

<script>
  console.log("hi");
</script>

You could compute a SHA 256 hash of the script content and add it to script-src:

script-src 'self' 'sha256-BASE64HASHVALUE';

Here’s a nonces example with WordPress:

add_action('send_headers', function() {
  $nonce = base64_encode(random_bytes(16));
  $policy = "default-src 'self'; script-src 'self' 'nonce-$nonce'; style-src 'self' 'unsafe-inline';";
  header("Content-Security-Policy: $policy");
  add_filter('script_loader_tag', function($tag, $handle) use ($nonce) {
    return str_replace('<script ', '<script nonce="' . esc_attr($nonce) . '" ', $tag);
  }, 10, 2);
});

This example adds the nonce to enqueued scripts. You still need to add the same nonce attribute to any inline scripts you output in templates.

Warning: Nonces in a mixed plugin environment take effort. If most of your inline scripts come from third-party plugins, hashes or carefully scoped ‘unsafe-inline’ may be the more practical choice.

Common WordPress CSP issues and fixes

Here are a few elements that may cause issues, and how to adjust for each one:

Page builders and themes with inline CSS

  • Problem: Layout looks unstyled.
  • Fix: Keep ‘unsafe-inline’ in style-src, or move to hashes if you control the inline blocks.

Third-party analytics and tag managers:

  • Problem: Console shows blocked scripts from Google or other tools.
  • Fix: Add their domains to script-src.

Payment and checkout scripts:

  • Problem: Checkout buttons fail or embedded payment frames don’t load.
  • Fix: Add payment domains to script-src, connect-src, and frame-src as needed.

Embedded media or social posts:

  • Problem: There are empty embed containers.
  • Fix: Add provider domains to frame-src or img-src.

A quick overview

Here’s a quick summary of the steps to put a CSP into place and maintain it over time.

  1. Write a small policy first.
    • Start with default-src ‘self’.
    • Add only the directives you know you need, usually script-src, style-src, img-src, font-src, and connect-src.
    • Keep the allowlist short. Every domain should have a purpose.
  2. Run in report-only mode.
    • Set Content-Security-Policy-Report-Only.
    • Browse multiple pages, not only the homepage. Include posts, archives, forms, and checkout pages.
    • Collect reports in DevTools or a reporting endpoint.
  3. Triage violations.
    • Read violated-directive first. That tells you what to fix.
    • Check blocked-uri. Decide if the resource is trusted and needed.
    • If yes, add the source to the right directive. If no, leave it blocked.
    • Change one thing at a time, then retest.
  4. Enforce once reports are clear.
    • Switch to Content-Security-Policy with the same policy.
    • Clear caches, reload key pages, and confirm the site works.
    • Watch the console for any last surprises.
  5. Tighten carefully.
    • If you control inline code, move from ‘unsafe-inline’ to hashes or nonces.
    • If third-party plugins inject inline scripts you cannot change, accept the targeted risk and document it.
  6. Maintain the policy.
    • After adding plugins or new features, adapt for new sources.
    • Review the allowlist every few months and remove dead entries.

This setup takes time up front. But once it’s running, you’ll block a lot of common attacks and reduce mixed‑content issues. It will give your WordPress site a stronger security baseline.

How CSP fits with other WordPress security headers

Along with CSP, consider these security headers for WordPress:

  • X‑Frame‑Options: Prevents clickjacking by controlling which sites can embed your pages in iframes.
  • X‑Content‑Type‑Options: nosniff: Prevents MIME‑type sniffing attacks.
  • Referrer‑Policy: Limits how much referrer information is sent to external sites.
  • Strict‑Transport‑Security (HSTS): Forces browsers to use HTTPS only.

CSP focuses on what resources can be loaded, while these other headers control how browsers treat your site and where it can be embedded.

Frequently asked questions

What is a Content Security Policy (CSP)?

A Content Security Policy (CSP) is an HTTP header that tells a browser which scripts, styles, images, fonts, and other resources can load on a page.

In WordPress, CSP helps block malicious scripts and unsafe content from loading on your site. It works by listing allowed domains and types of resources, and any resource that does not match the rules is blocked by the browser. This makes it harder for attackers to inject harmful code into your WordPress site.

WordPress sites often use many plugins, themes, and third‑party services, so CSP can help protect forms, logins, payment pages, and admin areas. You can add CSP through plugins, server settings, or PHP code in your WordPress site.

Why should I add a CSP header to my WordPress site?

You should add a CSP header to reduce the risk of cross‑site scripting (XSS) attacks, data injection, and mixed‑content issues on your WordPress site. CSP limits which scripts and styles can run, so even if a harmful script is added to your page, the browser may not load it. This helps protect user data, login sessions, payment information, and admin areas.

CSP also helps enforce the use of HTTPS for resources and makes it easier to see which external domains your site depends on. Over time, you can tighten your policy and remove unnecessary domains, which can improve both security and site performance checks.

Is it safe to add a CSP header to a live WordPress site?

It can be safe to add a CSP header to a live WordPress site, but only if you test carefully first. If your policy is too strict, it can break forms, payment gateways, analytics, chat widgets, or page builder layouts. The safest approach is to start in report‑only mode, collect errors, and then apply the policy in enforcement mode once you are sure everything works.

For a live WordPress site, it is best to test on a staging copy if possible, or use a small portion of traffic. You should also keep a backup of your server config and .htaccess before making changes. If something breaks, you can quickly remove the header and restore the old settings.

How do I test a CSP policy in WordPress before enforcing it?

You test a CSP policy in WordPress by using Content-Security-Policy-Report-Only instead of Content-Security-Policy. This lets the browser follow the rules and log violations without blocking content. You can then check the browser’s developer console or a reporting service to see which resources are blocked.

For example, you can add a header like:

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self';

Then visit your site on different pages, including forms, checkout pages, and the admin area. Look for errors in the console and adjust the allowed domains. Once you see few or no violations, you can change the header to Content-Security-Policy and gradually tighten it.

Why does my CSP break Google Analytics, Facebook Pixel, or other scripts?

CSP can break Google Analytics, Facebook Pixel, or other scripts because those services load from external domains not included in your policy. For example, if your CSP only allows script-src ‘self’, any script from http://www.google-analytics.com or connect.facebook.net will be blocked. The browser will refuse to load them, and the tracking will stop.

To fix this, you need to add the required domains to your policy. For example:

script-src 'self' www.google-analytics.com connect.facebook.net;

You should also avoid using unsafe-inline unless absolutely necessary. If you use inline scripts, you can switch to nonces or hashes for better security.

Should I allow unsafe-inline in my WordPress CSP policy?

You should avoid unsafe-inline in your WordPress CSP policy unless you have no other choice. Allowing unsafe-inline means the browser can run inline scripts and styles, which opens the door to certain types of cross‑site scripting attacks. If your site uses page builders, plugins, or themes that rely heavily on inline scripts, CSP may break layouts or functionality.

If you must allow unsafe-inline, you should document why and review it regularly. A better long‑term option is to migrate to nonces or hashes for trusted inline scripts. You can also use a more strict policy for the front‑end and a separate, less strict policy for the admin area.

How do nonces and hashes work in a WordPress CSP?

Nonces and hashes let you allow specific inline scripts without enabling unsafe-inline for your whole site. A nonce is a short, random value that changes with each page load. You add that nonce to your CSP and then include it in the script tag. The browser only runs scripts that have the correct nonce.

Hashes work similarly. You compute a cryptographic hash of the script’s content and add that hash to your CSP. The browser checks if the script matches the hash and only runs it if it does. In WordPress, some plugins and themes can generate nonces or hashes for you, or you can add them manually in your CSP header and in your template files.

Can I use a plugin to add CSP to WordPress instead of server code?

You can use a plugin to add CSP to WordPress instead of editing server code. Many security or header plugins let you set a Content-Security-Policy header from the dashboard. This is useful if you are not comfortable editing .htaccess, Nginx config, or PHP files. Plugins often provide a simple interface to add allowed domains and test settings.

However, plugins may not support every directive or advanced CSP feature. They may also add overhead if they generate headers on every request. If you run multiple WordPress sites or need fine‑grained control, server‑level CSP (Apache or Nginx) is usually more efficient and easier to manage for advanced users.

How often should I update my CSP policy on a WordPress site?

You should review and update your CSP policy on a WordPress site every 3–6 months, or whenever you add new plugins, themes, or third‑party services. Every time you install a new plugin, it may load scripts, styles, or fonts from new domains that are not in your current policy. If those domains are blocked, elements may break or fail to load.

During each review, you should check your browser console or CSP reporting service for new violation reports. You can then decide whether to allow the new domains, tighten existing rules, or remove domains that are no longer in use. Keeping your policy up to date helps maintain both security and functionality.

Does CSP replace other WordPress security measures like firewalls or backups?

CSP does not replace other WordPress security measures like firewalls, malware scanning, or backups. It is one layer among many. CSP helps control which resources the browser can load, but it does not stop brute‑force attacks, file changes, plugin vulnerabilities, or data loss. These risks are still handled by firewalls, login protection, malware scanners, and regular backups.

For a complete WordPress security setup, CSP should be combined with HTTPS, strong passwords, two‑factor authentication, security headers, and a security plugin like Jetpack Security. Together, these layers reduce the chance that an attack succeeds and increase the chance that you can recover quickly if something goes wrong.

This entry was posted in WordPress Tutorials. Bookmark the permalink.
WordPress Tutorials

Jen Swisher profile

Jen Swisher

Jen is a Customer Experience Specialist for Jetpack. She has been working with WordPress and Jetpack for over a decade. Before starting at Automattic, Jen helped small businesses, local non-profits, and Fortune 50 companies create engaging web experiences for their customers. She is passionate about teaching others how to create on the web without fear.

Security

We guard your site. You run your business.

Jetpack Security provides easy‑to‑use, comprehensive WordPress site security, including real‑time backups, a web application firewall, malware scanning, and spam protection.

Secure your site

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.