Skip to content

Instantly share code, notes, and snippets.

@joshuafredrickson
Last active March 12, 2025 21:08
Show Gist options
  • Save joshuafredrickson/f0a9101eb7014054a7486f015d68a7f9 to your computer and use it in GitHub Desktop.
Save joshuafredrickson/f0a9101eb7014054a7486f015d68a7f9 to your computer and use it in GitHub Desktop.
Add CSP to WordPress, including nonces for inline scripts
<?php
/**
* Plugin Name: Content Security Policy
* Version: 1.0.1
* Description: Adds a Content-Security-Policy header to all non-admin requests.
* Requires PHP: 8.1
* Requires at least: 5.7.0
* License: GNU General Public License v2
* License URI: http://www.gnu.org/licenses/gpl-2.0.html
* Original Inspiration: https://gist.github.com/westonruter/c8b49406391a8d86a5864fb41a523ae9
*/
namespace App;
/**
* Gets CSP nonce.
*/
function get_nonce(): string
{
static $nonce = null;
if ($nonce === null) {
$nonce = wp_create_nonce('csp');
}
return $nonce;
}
/**
* Gets Strict CSP header value.
*/
function get_csp_header_value(): string
{
return join(
'; ',
// Set your CSP below
[
"font-src 'self' data:",
"frame-src 'none'",
"img-src 'self' data: secure.gravatar.com",
"manifest-src 'self'",
sprintf("script-src-elem 'nonce-%s' 'self'", get_nonce()),
"style-src-elem 'self' 'unsafe-inline'", // Unfortunately, inline CSS is not currently filterable
"object-src 'none'",
"base-uri 'none'", // Note: jQuery can violate this in jQuery.parseHTML() due to <https://github.com/jquery/jquery/issues/2965>.
"report-uri https://{{ your reporting uri }}",
]
);
}
/**
* Adds nonce attribute to script attributes.
*/
foreach (['wp_script_attributes', 'wp_inline_script_attributes'] as $hook) {
add_filter($hook, function (array $attributes): array {
$attributes['nonce'] = get_nonce();
return $attributes;
});
}
/**
* Sends Strict CSP header.
*/
add_action('login_init', function () {
header(sprintf('Content-Security-Policy: %s', get_csp_header_value()));
});
/**
* Send the header on the frontend and in the login screen.
*/
add_filter(
'wp_headers',
static function ($headers) {
$headers['Content-Security-Policy'] = get_csp_header_value();
return $headers;
}
);
@devendrabhandari
Copy link

Will it work for all the styles and scripts added by plugins and themes, as well as the inline scripts added by the theme?

@joshuafredrickson
Copy link
Author

Will it work for all the styles and scripts added by plugins and themes, as well as the inline scripts added by the theme?

✅ Inline scripts
✅ Enqueued scripts
⛔ Inline styles
✅ Enqueued styles

@jamminjames
Copy link

How do you install this? I put it in my Wordpress mu-plugins folder, as the plugin name implies, and it crashed the site.

@joshuafredrickson
Copy link
Author

joshuafredrickson commented Dec 12, 2024

@jamminjames Assuming you're running PHP 8.1+, my guess is that you aren't using anything in the Roots stack, so the call to add_filters is causing an error.

I updated the code above to not require that function, and it should now work on sites PHP 8.1+ and WP 5.7.0+. Also, remember to update the report-uri header in get_csp_header_value().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment