Tutorials

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.

S
Sarah Chen
9 min read
2,471 views
Complete guide to implementing WordPress security headers

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.

Share:
S
Written by Sarah Chen

WP Folder Shield Team

Related Articles

The Ultimate Guide to WordPress Security in 2026
The Ultimate Guide to WordPress Security in 2026

Learn how to protect your WordPress website from hackers, malware, and security threats with this...

January 15, 2026
How to Scan Your WordPress Site for SEO Spam and Hidden Malicious Content
How to Scan Your WordPress Site for SEO Spam and Hidden Malicious Content

Learn effective methods to scan your WordPress site for hidden SEO spam, malicious links, and...

January 13, 2026
How to Protect Your WordPress Uploads Folder from Malware
How to Protect Your WordPress Uploads Folder from Malware

The wp-content/uploads folder is one of the most vulnerable directories in WordPress. Learn how to...

January 13, 2026

Ready to Secure Your WordPress Site?

Get complete protection with WP Folder Shield.

Get Started