WordPress Security Headers
Security headers are special HTTP response headers that your WordPress site can send to browsers to enhance security and protect against various attacks. When properly implemented, these headers instruct browsers to enable built-in security mechanisms that help defend against common vulnerabilities like cross-site scripting (XSS), clickjacking, and other code injection attacks.
Why Security Headers Matter
In today's threat landscape, implementing proper security headers is no longer optional—it's a necessity. These headers provide an additional layer of defense even if vulnerabilities exist in your WordPress themes, plugins, or core files.
Here's why you should care about security headers:
- They help mitigate multiple security risks with minimal configuration
- Many headers can be implemented without affecting user experience
- They improve your site's security posture in security audits
- Some headers help protect your users' privacy and data
Common Security Headers for WordPress
Let's explore the most important security headers you should consider adding to your WordPress site:
1. Content-Security-Policy (CSP)
The Content-Security-Policy header is one of the most powerful security headers. It helps prevent cross-site scripting (XSS) attacks by controlling which resources (scripts, stylesheets, images, etc.) browsers are allowed to load.
Basic Example:
// Add this to your functions.php or a security plugin
function add_csp_header() {
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' https://trusted-cdn.com; img-src 'self' data: https://*; font-src 'self' https://trusted-font-cdn.com;");
}
add_action('send_headers', 'add_csp_header');
This header tells browsers to:
- Only load scripts from your own domain and trusted-cdn.com
- Only load styles from your own domain and trusted-cdn.com
- Load images from your domain, data URIs, and any HTTPS source
- Load fonts only from your domain and a trusted font CDN
For WordPress sites with many external resources, implementing CSP can be challenging. Start with a permissive policy in report-only mode:
function add_csp_report_only() {
header("Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; report-uri https://your-reporting-endpoint.com");
}
add_action('send_headers', 'add_csp_report_only');
This allows you to see violations without breaking functionality.
2. X-XSS-Protection
This header enables built-in browser protection against reflected XSS attacks.
function add_xss_protection() {
header("X-XSS-Protection: 1; mode=block");
}
add_action('send_headers', 'add_xss_protection');
While modern browsers rely more on CSP than this older header, it provides some protection for users on older browsers.
3. X-Frame-Options
This header helps prevent clickjacking attacks by controlling which sites can embed yours in an iframe.
function add_frame_options() {
header("X-Frame-Options: SAMEORIGIN");
}
add_action('send_headers', 'add_frame_options');
Options include:
DENY
: Prevents any site from framing your contentSAMEORIGIN
: Only allows your own site to frame your contentALLOW-FROM https://example.com
: Allows specific domains to frame your content (deprecated)
4. X-Content-Type-Options
Prevents browsers from MIME-sniffing a response away from the declared content type.
function add_content_type_options() {
header("X-Content-Type-Options: nosniff");
}
add_action('send_headers', 'add_content_type_options');
This prevents browsers from interpreting files as a different MIME type than what is specified in the Content-Type header.
5. Strict-Transport-Security (HSTS)
Forces browsers to use HTTPS for your site even if users type "http://" in the address bar.
function add_hsts_header() {
header("Strict-Transport-Security: max-age=31536000; includeSubDomains; preload");
}
add_action('send_headers', 'add_hsts_header');
Parameters:
max-age
: How long browsers should remember to use HTTPS (in seconds)includeSubDomains
: Applies the policy to all subdomainspreload
: Indicates intent to submit to the HSTS preload list (optional)
Only implement HSTS after confirming your site works properly over HTTPS, as it can make your site inaccessible if SSL is misconfigured.
6. Referrer-Policy
Controls how much referrer information should be included with requests.
function add_referrer_policy() {
header("Referrer-Policy: strict-origin-when-cross-origin");
}
add_action('send_headers', 'add_referrer_policy');
Common values:
no-referrer
: No referrer information is sentno-referrer-when-downgrade
: Don't send referrer when HTTPS → HTTPsame-origin
: Only send referrer for same-site requestsstrict-origin-when-cross-origin
: Send full path for same-origin, only domain for cross-origin
7. Permissions-Policy (formerly Feature-Policy)
Controls which browser features and APIs can be used.
function add_permissions_policy() {
header("Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=()");
}
add_action('send_headers', 'add_permissions_policy');
This example:
- Blocks camera access completely
- Blocks microphone access completely
- Allows geolocation only for your own domain
- Blocks payment API usage
Implementing Security Headers in WordPress
There are several ways to implement security headers in WordPress:
1. Using PHP in functions.php
You can add the headers directly in your theme's functions.php file:
function add_security_headers() {
// X-Content-Type-Options
header("X-Content-Type-Options: nosniff");
// X-Frame-Options
header("X-Frame-Options: SAMEORIGIN");
// X-XSS-Protection
header("X-XSS-Protection: 1; mode=block");
// Referrer-Policy
header("Referrer-Policy: strict-origin-when-cross-origin");
// Permissions-Policy
header("Permissions-Policy: camera=(), microphone=(), geolocation=(self)");
}
add_action('send_headers', 'add_security_headers');
2. Using the .htaccess File (Apache)
If you're on an Apache server, you can add headers via .htaccess:
<IfModule mod_headers.c>
# Security Headers
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "strict-origin-when-cross-origin"
Header set Permissions-Policy "camera=(), microphone=(), geolocation=(self)"
# Only add HSTS if you're confident in your HTTPS setup
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>
3. Using nginx Configuration
If your site runs on nginx:
# Add this to your server block
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(self)";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
4. Using Security Plugins
Several WordPress security plugins make it easy to add security headers:
- Sucuri Security: Includes options for security headers
- Really Simple SSL: Handles HTTPS-related headers
- WP Rocket: Pro version includes security headers
- SecuPress: Comprehensive security including headers
Testing Your Security Headers
After implementing security headers, it's essential to test them. Here are some tools you can use:
- Mozilla Observatory - A comprehensive security scanner
- SecurityHeaders.com - Specifically analyzes HTTP security headers
- Chrome DevTools - Inspect network requests to see your headers
Real-World Example: E-commerce Site Security Headers
For a WordPress e-commerce site running WooCommerce, you might implement the following:
function ecommerce_security_headers() {
// Basic security headers
header("X-Content-Type-Options: nosniff");
header("X-Frame-Options: SAMEORIGIN");
header("X-XSS-Protection: 1; mode=block");
// Strict referrer policy to protect customer information
header("Referrer-Policy: strict-origin-when-cross-origin");
// Allow payment APIs only on checkout pages
if (is_page('checkout')) {
header("Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self)");
} else {
header("Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()");
}
// CSP tailored for e-commerce
$csp = "default-src 'self'; ";
$csp .= "script-src 'self' https://js.stripe.com https://www.paypal.com https://www.googletagmanager.com; ";
$csp .= "style-src 'self' 'unsafe-inline'; ";
$csp .= "img-src 'self' data: https://*; ";
$csp .= "connect-src 'self' https://api.stripe.com; ";
$csp .= "frame-src 'self' https://js.stripe.com https://www.paypal.com; ";
header("Content-Security-Policy: " . $csp);
// HSTS for secure connections
header("Strict-Transport-Security: max-age=31536000; includeSubDomains");
}
add_action('send_headers', 'ecommerce_security_headers');
This setup:
- Implements basic protections across the site
- Only enables payment APIs on the checkout page
- Allows necessary third-party resources for payment processing
- Ensures all transactions occur over HTTPS
Handling Common Issues
Mixed Content Warnings
If you see mixed content warnings after implementing CSP:
// Temporarily allow mixed content while fixing issues
function debug_csp_header() {
header("Content-Security-Policy: upgrade-insecure-requests; default-src https: 'unsafe-inline' 'unsafe-eval';");
}
add_action('send_headers', 'debug_csp_header');
The upgrade-insecure-requests
directive attempts to upgrade HTTP requests to HTTPS.
Third-party Plugin Conflicts
If third-party plugins stop working after adding security headers, you may need to add exceptions:
function add_plugin_compatible_csp() {
// More permissive CSP for admin area
if (is_admin()) {
header("Content-Security-Policy: default-src 'self' 'unsafe-inline' 'unsafe-eval' https:; img-src 'self' data: https:;");
} else {
// Stricter CSP for front-end
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.com; style-src 'self' 'unsafe-inline';");
}
}
add_action('send_headers', 'add_plugin_compatible_csp');
Security Headers Flow
Summary
Security headers are a powerful and relatively easy way to enhance your WordPress site's security posture. By implementing the headers covered in this guide, you can protect your site and users from various common attacks.
Remember these key points:
- Start with simpler headers like X-Frame-Options and X-Content-Type-Options
- Use Content-Security-Policy in report-only mode first to avoid breaking functionality
- Test your headers after implementation
- Adjust headers for special cases (admin area, checkout pages, etc.)
- Regularly review and update your security headers
Additional Resources
Practice Exercise
- Test your current WordPress site with SecurityHeaders.com and note your score.
- Implement the basic headers (X-Content-Type-Options, X-Frame-Options, and X-XSS-Protection).
- Test again and observe the improvement.
- Create a Content-Security-Policy-Report-Only header and monitor for violations.
- Gradually tighten your CSP based on observed violations until you have a secure policy that doesn't break functionality.
By systematically implementing these security headers, you'll significantly improve your WordPress site's security with minimal effort.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)