Title: fepo CSP &amp; Security Headers
Author: fepo
Published: <strong>October 28, 2026</strong>
Last modified: October 29, 2026

---

Search plugins

![](https://ps.w.org/fepo-csp-security-headers/assets/banner-772x250.png?rev=3771771)

![](https://ps.w.org/fepo-csp-security-headers/assets/icon-256x256.png?rev=3771727)

# fepo CSP & Security Headers

 By [fepo](https://profiles.wordpress.org/fepo/)

[Download](https://downloads.wordpress.org/plugin/fepo-csp-security-headers.1.0.2.zip)

 * [Details](https://wordpress.org/plugins/fepo-csp-security-headers/#description)
 * [Reviews](https://wordpress.org/plugins/fepo-csp-security-headers/#reviews)
 *  [Installation](https://wordpress.org/plugins/fepo-csp-security-headers/#installation)
 * [Development](https://wordpress.org/plugins/fepo-csp-security-headers/#developers)

 [Support](https://wordpress.org/support/plugin/fepo-csp-security-headers/)

## Description

A Content-Security-Policy (CSP) tells browsers which sources a page may load scripts,
styles, fonts, images and frames from. Writing one by hand for a WordPress site 
with a theme, a page builder, a shop and a cookie banner is tedious and error-prone.
This plugin does the legwork for you.

**How it works**

 1. Click “Start scan”. Your browser opens every page of your site in a hidden frame—
    the home page, shop pages, the newest entry of each post type and the rest of your
    content (up to 500 pages).
 2. For each page the scan records which external sources it loads and which inline
    scripts and styles it contains.
 3. From all pages together the plugin builds one policy. Inline code is allowed by
    hash when it is stable; when it changes per visitor (for example because it carries
    a WordPress nonce) or uses inline event handlers, the policy falls back to `'unsafe-
    inline'` instead of breaking your pages.
 4. The policy is sent as a **Report-Only** header first. Browsers report what the 
    policy would have blocked, nothing is actually blocked. Reported sources show up
    on the “Reports” tab and can be added to the policy with one click.
 5. The plugin remembers which sources each page needs. The “Pages” tab shows when 
    a page was last scanned and whether it changed since. Rescan a single page, or 
    all changed and new pages at once, instead of the whole site.
 6. Once no new reports come in, switch the mode to **Enforce** on the “Policy” tab.
    Browsers then block everything the policy does not allow.

**What you get**

 * Content-Security-Policy in Report-Only or Enforce mode, sent as an HTTP header(
   never as a meta tag). Enforce warns you while reported sources are still missing
   from the policy.
 * Reports endpoint on your own site (`/wp-json/fepo-csp/v1/report`), stored without
   IP address or user agent and deleted after 30 days.
 * Further security headers: `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-
   Policy`, `X-Frame-Options` and, if you confirm your site runs on HTTPS only, `
   Strict-Transport-Security` in two stages.
 * Headers that your server already sends are detected and not sent a second time.
 * On Apache and LiteSpeed Enterprise the headers can be written into a marked block
   of your `.htaccess`; the plugin then checks that they really arrive and removes
   the block again if your server ignores it. For nginx, OpenLiteSpeed, Caddy and
   IIS you get the matching configuration text to copy.
 * WooCommerce: optionally puts one product into your own cart so the checkout can
   be scanned too (removed afterwards), and adds the documented sources of active
   payment providers (Stripe, WooPayments, PayPal, Mollie, Klarna, Adyen).
 * Clicks “Accept” on common cookie banners during the scan, so that trackers loaded
   after consent are part of the policy.
 * “Pages” tab: every scanned page with its last scan time and status — up to date,
   changed since the last scan, or not scanned yet (for example a new post).
 * Rescan one page with its “Rescan” button, or all changed and new pages with “
   Rescan all changed and new pages” (up to 50 per run).
 * Unused sources: when no scanned page needs a source any more (for example after
   you deleted the post with a video embed), the “Policy” tab lists it with a “Remove”
   button. Nothing is removed automatically.
 * Deleted or unpublished posts leave the page list by themselves.

### Privacy

The plugin itself sends no data to fepo or to any third party.

 * The scan runs in your logged-in admin browser and opens your pages the way a 
   visitor would. Everything embedded in those pages loads as usual — including 
   third-party services and trackers, and also those that only load after the “Accept”
   click on your cookie banner, which the scan performs. These services receive 
   the usual request data from your browser, just as on a normal visit.
 * While a scan runs, its page views can therefore show up in your statistics (for
   example in an analytics tool), and the “Accept” click may change your own consent
   choice in this browser.
 * To find out which headers your server already sends, the plugin requests your
   own home page from your server (when activated, from the plugin page, after a
   scan and once a day). After writing to `.htaccess` it also requests your home
   page and `wp-admin` once to make sure the server still answers, and your home
   page up to three more times to make sure the headers arrive.
 * The page list is stored in your own database: the page address, the sources and
   inline-code hashes the page needs, the time of the scan and the last-modified
   date of the post — no visitor data.
 * Browser reports go to your own site. They are only accepted while the plugin’s
   policy is active and only for pages of your own site. The plugin stores the directive,
   the origin of the blocked source, the path of the page, a counter and two timestamps—
   no IP address, no user agent, no cookies — and deletes them after 30 days.
 * The plugin sets no cookies of its own and loads no external scripts, fonts or
   images.

### External services

This plugin does not connect to any external service. It sends no data to fepo or
to any third party.

 * The only requests the plugin makes go to your own site: it requests your own 
   home page (and `wp-admin` after writing to `.htaccess`) to see which headers 
   your server sends. See “Privacy” above.
 * The WooCommerce payment providers (Stripe, WooPayments, PayPal, Mollie, Klarna,
   Adyen) are not contacted by the plugin. Their documented domains are only written
   into your Content-Security-Policy as allowed sources, so that your checkout keeps
   working when the policy is active. The links in `data/payment-providers.php` 
   point to the providers’ public CSP documentation they were taken from.

## Screenshots

[⌊Scan: your browser opens up to 500 pages of your site and records what each page
loads. Optionally get an email when it is finished, or scan the WooCommerce checkout
too.⌉⌊Scan: your browser opens up to 500 pages of your site and records what each
page loads. Optionally get an email when it is finished, or scan the WooCommerce
checkout too.⌉[

Scan: your browser opens up to 500 pages of your site and records what each page
loads. Optionally get an email when it is finished, or scan the WooCommerce checkout
too.

[⌊Pages: every page with its last scan and status (up to date, changed since the
last scan, not scanned yet). Rescan one page or all changed and new pages at once.⌉⌊
Pages: every page with its last scan and status (up to date, changed since the last
scan, not scanned yet). Rescan one page or all changed and new pages at once.⌉[

Pages: every page with its last scan and status (up to date, changed since the last
scan, not scanned yet). Rescan one page or all changed and new pages at once.

[⌊Policy: choose Off, Report-Only or Enforce, review the generated policy, remove
sources no page needs any more, and send the headers via .htaccess or copy the configuration
for your web server.⌉⌊Policy: choose Off, Report-Only or Enforce, review the generated
policy, remove sources no page needs any more, and send the headers via .htaccess
or copy the configuration for your web server.⌉[

Policy: choose Off, Report-Only or Enforce, review the generated policy, remove 
sources no page needs any more, and send the headers via .htaccess or copy the configuration
for your web server.

[⌊Reports: sources that browsers reported as blocked, added to the policy with one
click.⌉⌊Reports: sources that browsers reported as blocked, added to the policy 
with one click.⌉[

Reports: sources that browsers reported as blocked, added to the policy with one
click.

[⌊Headers: further security headers with an explanation of each switch, HSTS in 
two stages.⌉⌊Headers: further security headers with an explanation of each switch,
HSTS in two stages.⌉[

Headers: further security headers with an explanation of each switch, HSTS in two
stages.

## Installation

 1. Upload the plugin folder to `/wp-content/plugins/`, or install the ZIP via “Plugins
    Add New  Upload Plugin”.
 2. Activate the plugin through the “Plugins” menu.
 3. Open “fepo  CSP & Headers” in the admin menu and start a scan.
 4. Check the result on the “Policy” tab. The policy is sent in Report-Only mode; watch
    the “Reports” tab for a few days, then switch the mode to Enforce.

## FAQ

### Do I have to keep the tab open during the scan?

Yes. The scan runs in the browser tab where you started it. It keeps running while
the tab is in the background, so you can work in another tab. If you close or reload
the tab, the plugin page offers “Continue scan” and picks up where it stopped.

### I use a page cache. What do I need to know?

Cached pages may keep the headers they were stored with. Clear the cache after a
scan and after changing headers. The plugin shows a hint when it detects a known
cache plugin (WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, WP Fastest
Cache, SiteGround Optimizer, Cloudflare).

### My server or another plugin already sends a Content-Security-Policy.

Then this plugin does not send its own policy, because browsers apply both policies
and the stricter result can break your site. Remove the other header first; the “
Policy” tab shows a warning as long as it is present. Other security headers that
already exist are left alone in the same way.

### Will the policy break my site?

Not in Report-Only mode: nothing is blocked, browsers only report. The plugin never
switches to Enforce by itself, and a new scan does not change the mode once you 
chose Enforce. Switch to Enforce on the “Policy” tab when the “Reports” tab stays
quiet; if something stops working, switch back to Report-Only and add the reported
source.

### Why are no security headers sent right after activation?

Nothing changes on your site until you decide: the security headers stay off until
you save the “Headers” tab or a scan finishes. The scan then switches on `X-Content-
Type-Options`, `Referrer-Policy` and — if your server does not send it already —`
X-Frame-Options`. `Permissions-Policy` stays off unless you switch it on, because
it disables camera, microphone and location for your pages. If the plugin page says“
Headers are not sent yet”, your server could not load its own home page; the notice
lists common causes.

### Why don’t I see the Content-Security-Policy header when I am logged in?

When the plugin sends the headers itself, logged-in users do not get the policy:
the admin bar and the block editor load their own inline code, which would otherwise
fill the reports. Log out or use a private window to check the header. Headers sent
by your web server behave differently: the `.htaccess` block (Apache, LiteSpeed 
Enterprise) and the configuration text for nginx, OpenLiteSpeed, Caddy and IIS reach
every visitor, logged in or not, but they keep the policy away from `wp-admin` and
the login page.

### My web server is nginx, OpenLiteSpeed, Caddy or IIS. What is different?

Nothing you have to do: the plugin sends all headers itself through WordPress, on
any web server. The configuration text on the “Policy” tab is optional; with it,
your web server sends the headers instead, also for files that WordPress does not
deliver. Like the `.htaccess` block, the text sends the policy everywhere except`
wp-admin` and the login page. If you use it, keep two things in mind. The plugin
cannot change your server configuration, so copy the updated text again after a 
new scan or after adding a source from the “Reports” tab. As soon as your server
sends a Content-Security-Policy, the plugin stops sending its own (click “Re-check
headers” after changing the server). On nginx, also copy the lines into every `location`
block that has `add_header` lines of its own, because nginx does not pass them on
there.

### Why does my policy contain ‘unsafe-eval’?

Pages with WordPress JavaScript templates (`wp.template`), for example WooCommerce
variable products, compile those templates at runtime. That needs `'unsafe-eval'`
in `script-src`, so the scan adds it when it finds such templates.

### Why does my policy contain ‘unsafe-inline’?

Many WordPress plugins print inline scripts that contain a nonce. Nonces change 
for every visitor and at least once a day, so a hash of such a script would be wrong
for the next visitor. In that case, and when pages use inline event handlers such
as `onload="…"`, the policy allows inline code instead of listing hashes. Style 
attributes (`style="…"`) have the same effect on `style-src`. With more than 30 
different inline scripts or styles the header would get too long, so the policy 
uses `'unsafe-inline'` there as well and the “Policy” tab says so.

### Do I have to run a full scan after every change?

No. The “Pages” tab marks posts that were edited after their last scan as “Changed
since last scan” and new posts as “Not scanned yet”. Use the “Rescan” button of 
a page or “Rescan all changed and new pages”. A full scan from the “Scan” tab is
still useful after you changed your theme or a plugin that affects every page.

### Why is a source listed as unused, and why is it still in my policy?

A source is unused when no scanned page loads it any more, for example after you
removed an embedded video. The plugin never removes sources by itself, because a
page that was skipped during a scan or a page you did not rescan may still need 
it. Check the list and click “Remove” for the sources you no longer need. Sources
of active payment providers and sources you added from the “Reports” tab are never
listed as unused.

### Why is my admin area or front page address important?

The scan opens your pages in a frame of the admin page. Browsers only allow that
when both use the same protocol, host and port. If they differ, the scan is not 
offered.

### Does it work on a multisite network?

Yes. Each site has its own scan, policy and settings, and the plugin sends the headers
for each site itself, on any web server. Only the optional `.htaccess` block is 
shared: on Apache and LiteSpeed Enterprise all sites use one `.htaccess` file, so
only a super admin can write the headers there; site admins get the text to copy
instead. On nginx, OpenLiteSpeed, Caddy and IIS the plugin never writes server files;
you get the configuration text to copy.

### What happens when I deactivate or delete the plugin?

Deactivating stops all headers the plugin sends and removes its block from your `.
htaccess` (Apache, LiteSpeed Enterprise). Configuration text you copied into nginx,
OpenLiteSpeed, Caddy or IIS stays in your server configuration until you remove 
it yourself. Deleting the plugin also removes its tables, options and the per-user
settings it stored, on every site of a multisite network. If your `.htaccess` is
no longer writable, the plugin cannot remove its block; the plugin page and the 
Plugins screen then tell you which lines to delete by hand.

## Reviews

There are no reviews for this plugin.

## Contributors & Developers

“fepo CSP & Security Headers” is open source software. The following people have
contributed to this plugin.

Contributors

 *   [ fepo ](https://profiles.wordpress.org/fepo/)
 *   [ michaelpehl ](https://profiles.wordpress.org/michaelpehl/)

[Translate “fepo CSP & Security Headers” into your language.](https://translate.wordpress.org/projects/wp-plugins/fepo-csp-security-headers)

### Interested in development?

[Browse the code](https://plugins.trac.wordpress.org/browser/fepo-csp-security-headers/),
check out the [SVN repository](https://plugins.svn.wordpress.org/fepo-csp-security-headers/),
or subscribe to the [development log](https://plugins.trac.wordpress.org/log/fepo-csp-security-headers/)
by [RSS](https://plugins.trac.wordpress.org/log/fepo-csp-security-headers/?limit=100&mode=stop_on_copy&format=rss).

## Changelog

#### 1.0.2

 * The configuration text for nginx, OpenLiteSpeed, Caddy and IIS keeps the policy
   away from `wp-admin` and the login page, like the `.htaccess` block (tested on
   nginx, OpenLiteSpeed and Caddy). For IIS the text now goes into the existing `
   <system.webServer>` section, because a second one is a configuration error.
 * Multisite: deleting the plugin removes the tables and options of every site, 
   and network deactivation clears the scheduled cleanup on every site.
 * A warning on the plugin page and on the Plugins screen when the `.htaccess` holds
   the plugin’s block but is no longer writable.
 * The shared fepo menu lists this plugin with a link to wordpress.org.

#### 1.0.1

 * The hint on the Scan and Pages tabs is now bold and says that closing the tab
   pauses the scan (a full scan of 500 pages takes about half an hour).
 * New mode “Enforce” on the “Policy” tab: browsers block what the policy does not
   allow. A warning shows while reported sources are still missing from the policy;
   a new scan keeps the mode.
 * New readme section “External services”.

#### 1.0.0

 * Initial release.
 * Scan of up to 500 pages in your browser, one Content-Security-Policy in Report-
   Only mode, reports endpoint and “Reports” tab.
 * Security headers with detection of headers your server already sends; `.htaccess`
   block on Apache and LiteSpeed Enterprise, configuration text for other servers.
 * “Pages” tab with the stored sources of every scanned page, status up to date /
   changed / not scanned yet, single rescan and “Rescan all changed and new pages”.
 * Unused sources on the “Policy” tab, removed only on your click.
 * WooCommerce checkout scan and payment provider sources.

## Meta

 *  Version **1.0.2**
 *  Last updated **2 days ago**
 *  Active installations **Fewer than 10**
 *  WordPress version ** 6.0 or higher **
 *  Tested up to **7.1.4**
 *  PHP version ** 7.4 or higher **
 * Tags
 * [content security policy](https://wordpress.org/plugins/tags/content-security-policy/)
   [csp](https://wordpress.org/plugins/tags/csp/)[headers](https://wordpress.org/plugins/tags/headers/)
   [security](https://wordpress.org/plugins/tags/security/)[Security Headers](https://wordpress.org/plugins/tags/security-headers/)
 *  [Advanced View](https://wordpress.org/plugins/fepo-csp-security-headers/advanced/)

## Ratings

No reviews have been submitted yet.

[Your review](https://wordpress.org/support/plugin/fepo-csp-security-headers/reviews/#new-post)

[See all reviews](https://wordpress.org/support/plugin/fepo-csp-security-headers/reviews/)

## Contributors

 *   [ fepo ](https://profiles.wordpress.org/fepo/)
 *   [ michaelpehl ](https://profiles.wordpress.org/michaelpehl/)

## Support

Got something to say? Need help?

 [View support forum](https://wordpress.org/support/plugin/fepo-csp-security-headers/)