WordPress Security Headers Deep Dive: Complete Implementation Guide
Master HTTP security headers for WordPress. Learn to implement Content-Security-Policy, X-Frame-Options, and other headers for maximum protection.
HTTP security headers form a critical defense layer that instructs browsers how to handle your WordPress site content. Properly configured headers prevent XSS, clickjacking, and other client-side attacks.
Essential Security Headers
Every WordPress site should implement these headers:
- Content-Security-Policy (CSP)
- X-Frame-Options
- X-Content-Type-Options
- Strict-Transport-Security (HSTS)
- Referrer-Policy
- Permissions-Policy
Content-Security-Policy
CSP is the most powerful header for preventing XSS attacks:
// Basic CSP implementation
function add_content_security_policy() {
$csp_directives = array(
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://www.google-analytics.com",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' https://fonts.gstatic.com",
"img-src 'self' data: https:",
"connect-src 'self' https://www.google-analytics.com",
"frame-ancestors 'self'",
"base-uri 'self'",
"form-action 'self'",
);
$csp = implode('; ', $csp_directives);
header("Content-Security-Policy: " . $csp);
}
add_action('send_headers', 'add_content_security_policy');
CSP for WordPress Admin
The admin area requires special CSP considerations:
function add_admin_csp() {
if (!is_admin()) {
return;
}
$admin_csp = array(
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval'",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https: blob:",
"font-src 'self' data:",
"frame-src 'self' https://wordpress.org",
"connect-src 'self' https://api.wordpress.org",
);
header("Content-Security-Policy: " . implode('; ', $admin_csp));
}
add_action('send_headers', 'add_admin_csp');
X-Frame-Options
Prevent clickjacking by controlling iframe embedding:
function add_frame_options() {
// Prevent all framing
header('X-Frame-Options: DENY');
// Or allow only same origin
// header('X-Frame-Options: SAMEORIGIN');
}
add_action('send_headers', 'add_frame_options');
Strict-Transport-Security
Force HTTPS connections:
function add_hsts_header() {
if (is_ssl()) {
// max-age in seconds (1 year recommended)
$max_age = 31536000;
// Include subdomains
$header = "Strict-Transport-Security: max-age={$max_age}; includeSubDomains";
// Add preload if submitting to HSTS preload list
// $header .= '; preload';
header($header);
}
}
add_action('send_headers', 'add_hsts_header');
X-Content-Type-Options
Prevent MIME type sniffing:
function add_content_type_options() {
header('X-Content-Type-Options: nosniff');
}
add_action('send_headers', 'add_content_type_options');
Referrer-Policy
Control referrer information shared with other sites:
function add_referrer_policy() {
// Strict: only send origin for cross-origin, full URL for same-origin
header('Referrer-Policy: strict-origin-when-cross-origin');
// Other options:
// header('Referrer-Policy: no-referrer'); // Never send referrer
// header('Referrer-Policy: origin'); // Only send origin
}
add_action('send_headers', 'add_referrer_policy');
Permissions-Policy
Control browser features and APIs:
function add_permissions_policy() {
$permissions = array(
'accelerometer=()',
'camera=()',
'geolocation=()',
'gyroscope=()',
'magnetometer=()',
'microphone=()',
'payment=()',
'usb=()',
);
header('Permissions-Policy: ' . implode(', ', $permissions));
}
add_action('send_headers', 'add_permissions_policy');
Combined Implementation
Add all security headers in one function:
function add_all_security_headers() {
// Skip for admin AJAX requests
if (defined('DOING_AJAX') && DOING_AJAX) {
return;
}
$headers = array(
'X-Frame-Options' => 'SAMEORIGIN',
'X-Content-Type-Options' => 'nosniff',
'X-XSS-Protection' => '1; mode=block',
'Referrer-Policy' => 'strict-origin-when-cross-origin',
);
// Add HSTS only on HTTPS
if (is_ssl()) {
$headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains';
}
// Add CSP
$csp = "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;";
$headers['Content-Security-Policy'] = $csp;
foreach ($headers as $name => $value) {
header("{$name}: {$value}");
}
}
add_action('send_headers', 'add_all_security_headers');
Testing Headers
Verify headers are applied correctly:
function verify_security_headers() {
$url = home_url();
$response = wp_remote_head($url);
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$headers = wp_remote_retrieve_headers($response);
$required = array(
'x-frame-options',
'x-content-type-options',
'content-security-policy',
);
$results = array();
foreach ($required as $header) {
$results[$header] = isset($headers[$header]) ? 'Present' : 'Missing';
}
return $results;
}
Plugin Compatibility
Handle conflicts with page builders and plugins:
function conditional_csp() {
// Relax CSP for specific pages
$relaxed_pages = array(
'elementor',
'visual-composer',
'divi-builder',
);
$current_url = $_SERVER['REQUEST_URI'];
foreach ($relaxed_pages as $builder) {
if (strpos($current_url, $builder) !== false) {
// Use more permissive CSP for builders
header("Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval' data: blob:");
return;
}
}
// Standard CSP for regular pages
add_strict_csp();
}
add_action('send_headers', 'conditional_csp');
Reporting Violations
Set up CSP violation reporting:
// CSP with reporting
function csp_with_reporting() {
$csp = "default-src 'self'; report-uri /csp-report/";
header("Content-Security-Policy: " . $csp);
}
// Endpoint to receive reports
add_action('rest_api_init', function() {
register_rest_route('security/v1', '/csp-report', array(
'methods' => 'POST',
'callback' => 'handle_csp_report',
'permission_callback' => '__return_true',
));
});
function handle_csp_report($request) {
$report = $request->get_body();
$data = json_decode($report, true);
if (isset($data['csp-report'])) {
error_log('CSP Violation: ' . print_r($data['csp-report'], true));
}
return new WP_REST_Response(null, 204);
}
Conclusion
Security headers provide essential browser-level protection. Start with Report-Only mode to identify issues, then gradually tighten policies. Regular testing ensures headers remain effective.
Written by Sarah Chen
WP Folder Shield Team