Content Security Policy for WordPress: Complete Implementation
Implement Content Security Policy (CSP) headers in WordPress to prevent XSS attacks and control resource loading.
Content Security Policy is a powerful defense against XSS and data injection attacks. CSP tells browsers which sources are allowed to load content, blocking malicious scripts.
Understanding CSP
What CSP Controls
- Script sources (JavaScript)
- Style sources (CSS)
- Image sources
- Font sources
- Frame sources (iframes)
- Connect sources (AJAX, WebSocket)
- Form actions
How CSP Prevents XSS
Even if an attacker injects malicious script tags, CSP blocks execution because the source is not whitelisted.
Basic CSP Implementation
Minimal CSP Header
add_action('send_headers', 'wpfs_add_csp_header');
function wpfs_add_csp_header() {
$csp = "default-src 'self'; ";
$csp .= "script-src 'self'; ";
$csp .= "style-src 'self' 'unsafe-inline'; ";
$csp .= "img-src 'self' data:; ";
$csp .= "font-src 'self'; ";
$csp .= "frame-ancestors 'self';";
header("Content-Security-Policy: " . $csp);
}
CSP Directives Explained
default-src- Fallback for unspecified directivesscript-src- Allowed JavaScript sourcesstyle-src- Allowed CSS sourcesimg-src- Allowed image sourcesconnect-src- Allowed AJAX/WebSocket targetsframe-src- Allowed iframe sourcesframe-ancestors- Who can frame this page
WordPress-Specific CSP
Handling WordPress Requirements
function wpfs_wordpress_csp() {
$nonce = wp_create_nonce('csp-nonce');
$csp = array(
"default-src 'self'",
"script-src 'self' 'nonce-{$nonce}' 'strict-dynamic'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self' data:",
"connect-src 'self'",
"frame-src 'self' https://www.youtube.com https://player.vimeo.com",
"frame-ancestors 'self'",
"base-uri 'self'",
"form-action 'self'"
);
return implode('; ', $csp);
}
Inline Script Nonces
// Add nonce to inline scripts
add_filter('script_loader_tag', 'wpfs_add_script_nonce', 10, 3);
function wpfs_add_script_nonce($tag, $handle, $src) {
if (!$src) {
$nonce = wp_create_nonce('csp-nonce');
return str_replace('
Report-Only Mode
Test Before Enforcing
// Use Report-Only to test without breaking site
function wpfs_csp_report_only() {
$csp = wpfs_build_csp();
// Report violations without blocking
header("Content-Security-Policy-Report-Only: " . $csp);
}
// Report endpoint
add_action('rest_api_init', function() {
register_rest_route('wpfs/v1', '/csp-report', array(
'methods' => 'POST',
'callback' => 'wpfs_handle_csp_report',
'permission_callback' => '__return_true'
));
});
function wpfs_handle_csp_report($request) {
$report = $request->get_json_params();
// Log violation
error_log('CSP Violation: ' . print_r($report, true));
return new WP_REST_Response(null, 204);
}
Common Third-Party Sources
Google Services
// Google Analytics, Fonts, reCAPTCHA
$csp .= "script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://www.gstatic.com; ";
$csp .= "img-src 'self' https://www.google-analytics.com; ";
$csp .= "connect-src 'self' https://www.google-analytics.com; ";
$csp .= "font-src 'self' https://fonts.gstatic.com; ";
$csp .= "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; ";
$csp .= "frame-src https://www.google.com; ";
Social Media Embeds
// Twitter, Facebook, YouTube
$csp .= "frame-src https://www.youtube.com https://player.vimeo.com https://www.facebook.com https://platform.twitter.com; ";
$csp .= "script-src 'self' https://platform.twitter.com https://connect.facebook.net; ";
Handling Inline Styles
Options for Inline CSS
'unsafe-inline'- Allows all inline styles (less secure)- Nonces - Add nonce attribute to style tags
- Hashes - Whitelist specific inline style content
- External stylesheets - Move inline to external files
Troubleshooting CSP
Common Issues
- Plugins adding inline scripts
- Theme using inline styles
- Google Fonts blocked
- Admin area breaking
Debug Process
- Enable Report-Only mode
- Check browser console for violations
- Add required sources to policy
- Test thoroughly before enforcing
Exclude Admin Area
// Different CSP for admin
add_action('send_headers', 'wpfs_conditional_csp');
function wpfs_conditional_csp() {
if (is_admin()) {
// Relaxed policy for admin
$csp = "default-src 'self' 'unsafe-inline' 'unsafe-eval';";
} else {
// Strict policy for frontend
$csp = wpfs_strict_csp();
}
header("Content-Security-Policy: " . $csp);
}
Conclusion
Content Security Policy provides strong XSS protection when properly configured. Start with Report-Only mode, gradually tighten restrictions, and maintain whitelist of legitimate sources.
Written by Sarah Chen
WP Folder Shield Team