Security Headers Checklist for WordPress
How to add all six essential HTTP security headers to a WordPress site - using a plugin, your theme's functions.php, or your web server config (Nginx/Apache).
How to Add Security Headers to WordPress (The Ultimate Guide)
By SecHead Security Engineering Team | Last Updated: June 2026
Quick Answer: How to Add Security Headers to WordPress
To quickly add security headers to WordPress, the most reliable method is editing your server's configuration file. If you are using Apache, add the following directives to your .htaccess file:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"
</IfModule>
Alternatively, if you lack server access, you can use a reputable security plugin like Headers Security Advanced or add a custom snippet to your child theme's functions.php file using the send_headers action hook.
Introduction: The State of WordPress Security
WordPress powers over 40% of the web. Its massive market share makes it a prime target for automated attacks, cross-site scripting (XSS), clickjacking, and data injection. Despite its robust core security, WordPress does not set HTTP security headers by default.
If you run a security header scan on a fresh WordPress installation, you will likely receive a D or F grade. HTTP security headers instruct the browser on how to behave when interacting with your site, effectively locking down vulnerabilities that code-level fixes might miss.
As Web Developers, Security Engineers, and System Administrators, properly configuring your WordPress security headers is a non-negotiable step in your hardening process. This comprehensive guide will walk you through implementing these headers via Apache, Nginx, Node.js reverse proxies, functions.php, and WordPress plugins.
https://your-wordpress-site.com
[Padlock Icon] Connection is Secure
---
Security Headers Grade: A+
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The 6 Essential Security Headers for WordPress
Before diving into the implementation methods, let's understand the specific headers you need to add to your WordPress site and what they do.
1. Strict-Transport-Security (HSTS)
HSTS forces web browsers to communicate with your WordPress site exclusively over secure HTTPS connections. This prevents man-in-the-middle (MitM) attacks and SSL stripping.
2. X-Frame-Options (XFO)
This header protects your site against clickjacking by restricting whether your site can be rendered in a <frame>, <iframe>, <embed>, or <object>. For WordPress, setting this to SAMEORIGIN or DENY is recommended.
3. X-Content-Type-Options
Internet Explorer and Chrome previously used "MIME-sniffing" to guess a file's content type. This could allow an attacker to upload a malicious script disguised as an image. Setting this to nosniff forces the browser to respect the declared MIME type.
4. Referrer-Policy
Controls how much referring information is included with requests made from your site. strict-origin-when-cross-origin is the modern standard, ensuring full URLs are passed within your site, but only the domain is passed to external sites.
5. Permissions-Policy (formerly Feature-Policy)
This header restricts which browser features and APIs (like the camera, microphone, and geolocation) can be used on your site.
6. Content-Security-Policy (CSP)
CSP is the ultimate defense against XSS. It restricts the sources from which scripts, styles, images, and other resources can be loaded. Note: CSP is notoriously difficult to implement in WordPress due to its heavy reliance on inline scripts.
Method 1: Adding Headers via Apache (.htaccess)
If your WordPress site is hosted on an Apache server (which is highly common for shared and VPS hosting), modifying the .htaccess file is the most efficient and reliable way to implement security headers. Because this happens at the server level, it processes before PHP is even executed.
Step-by-Step Implementation
- Connect to your server via SSH or FTP.
- Locate the
.htaccessfile in the root directory of your WordPress installation (usuallypublic_htmlor/var/www/html/). - Open the file and append the following configuration:
<IfModule mod_headers.c>
# Enforce HTTPS
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Prevent Clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Prevent MIME sniffing
Header always set X-Content-Type-Options "nosniff"
# Control Referrer Information
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Restrict Browser Features
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=()"
# Content Security Policy (Caution: Test thoroughly on WordPress)
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; object-src 'none'; frame-ancestors 'self';"
</IfModule>
Verifying Apache Configuration
$ curl -I https://your-wordpress-site.com
HTTP/1.1 200 OK
Date: Mon, 22 Jun 2026 12:00:00 GMT
Server: Apache
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=(), usb=()
Content-Type: text/html; charset=UTF-8
Method 2: Adding Headers via Nginx
Nginx is favored by high-performance WordPress hosts (like Kinsta, WPEngine, or custom VPS setups). To add headers in Nginx, you must edit your site's server block.
Step-by-Step Implementation
- Access your server via SSH.
- Open your Nginx site configuration file, typically located at
/etc/nginx/sites-available/yourdomain.comor/etc/nginx/nginx.conf. - Inside the
server { ... }block that handles port 443 (HTTPS), add theadd_headerdirectives:
server {
listen 443 ssl http2;
server_name yourdomain.com;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=()" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; object-src 'none'; frame-ancestors 'self';" always;
# Rest of your WordPress config...
location / {
try_files $uri $uri/ /index.php?$args;
}
}
**CRITICAL WARNING:** Always use the `always` flag at the end of your Nginx `add_header` directives. Without it, Nginx will not send these headers with 4xx or 5xx error responses, leaving error pages vulnerable to XSS and framing attacks.
After modifying the configuration, test and reload Nginx:
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl reload nginx
Method 3: Node.js / Reverse Proxy Setup
If you are running a decoupled (headless) WordPress architecture, or fronting WordPress with a Node.js reverse proxy, you can set the headers at the proxy layer using middleware like Helmet.js.
const express = require('express');
const helmet = require('helmet');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Use Helmet for automatic security headers
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
objectSrc: ["'none'"],
upgradeInsecureRequests: [],
},
},
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
}));
// Proxy requests to backend WordPress
app.use('/', createProxyMiddleware({
target: 'https://backend-wordpress.internal',
changeOrigin: true
}));
app.listen(3000, () => {
console.log('Secure proxy running on port 3000');
});
Method 4: Using WordPress Functions.php
If you are on a managed host that restricts access to Nginx or Apache configuration files, you can inject security headers via PHP in your WordPress theme.
**BEST PRACTICE:** Never add this code to your parent theme's `functions.php`. Theme updates will overwrite your changes. Always use a Child Theme or a custom site-specific plugin.
Add this snippet to your child theme's functions.php:
function sechead_add_security_headers() {
// Prevent headers from interfering with the WordPress admin interface
if ( ! is_admin() ) {
header( 'Strict-Transport-Security: max-age=31536000; includeSubDomains; preload' );
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
header( 'Permissions-Policy: geolocation=(), microphone=(), camera=()' );
header( "Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; object-src 'none';" );
}
}
// Attach to the send_headers hook
add_action( 'send_headers', 'sechead_add_security_headers' );
Method 5: Using WP Security Plugins
For non-technical administrators, using a plugin is the easiest route. However, it is slightly less performant than server-level configuration because WordPress has to execute PHP to generate the headers.
Top Recommended Plugins:
- Headers Security Advanced & HSTS WP: A lightweight, dedicated plugin that solely focuses on adding headers.
- Solid Security (formerly iThemes Security): Offers a comprehensive suite of tools, including an advanced header management module.
- Wordfence Security: While primarily a WAF, it manages some headers automatically.
How to implement with a plugin:
- Navigate to Plugins > Add New in your WP Admin dashboard.
- Search for "Headers Security Advanced".
- Install and Activate the plugin.
- Navigate to the plugin settings and toggle on X-Frame-Options, X-Content-Type-Options, HSTS, and Referrer-Policy.
Deep Dive: The Nightmare of Content Security Policy (CSP) on WordPress
Implementing a strict Content Security Policy (CSP) is the crown jewel of web security, but applying it to WordPress is notoriously painful.
Why WordPress Breaks with Strict CSP
A secure, strict CSP completely bans inline scripts (<script>...</script>) and the eval() function. Unfortunately:
- Gutenberg (Block Editor) relies heavily on inline scripting.
- jQuery, embedded deep within WordPress core and thousands of themes, occasionally utilizes
eval(). - Tracking Scripts (Google Analytics, Meta Pixel) inject inline code.
- Caching/Minification Plugins often merge and inline scripts to boost PageSpeed scores.
If you deploy a strict CSP without unsafe-inline, your site will likely break. Menus will stop working, sliders will freeze, and the admin panel will go completely blank.
[Developer Console]
Error: Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.
The Pragmatic WordPress CSP Strategy
To balance security with functionality, use the following pragmatic CSP baseline:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.google-analytics.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com; object-src 'none'; frame-ancestors 'self';
While 'unsafe-inline' reduces the effectiveness of your CSP against XSS, explicitly defining your allowed external domains (like Google Analytics) still provides a massive reduction in attack surface compared to having no CSP at all.
Advanced tip: For maximum security, decouple your WordPress backend from the frontend. Use WordPress purely as a headless CMS, and build your frontend with Next.js or Astro. This allows you to apply a hyper-strict CSP on the frontend without breaking the WordPress admin interface.
Troubleshooting Common Issues
1. Headers Not Appearing in Scans
If you've added the headers but security scanners don't see them:
- Clear Your Caches: Clear WordPress caching plugins (WP Rocket, W3 Total Cache), clear your CDN cache (Cloudflare), and clear your server cache (Redis/Varnish).
- Check Cloudflare: If you use Cloudflare, it might be stripping or overwriting your headers. You may need to set headers using Cloudflare Transform Rules or Cloudflare Workers.
2. "Multiple Headers Found" Warning
If you use both a security plugin and server-level .htaccess rules, you will send duplicate headers. This can confuse browsers. Pick one method and stick to it. Remove the plugin if you successfully implement server-level directives.
3. Site Stuck in a Redirect Loop after adding HSTS
If you enable HSTS (Strict-Transport-Security) but your SSL certificate is invalid or your site is forcing a redirect to HTTP somewhere in your WordPress settings, you will create an infinite redirect loop. Ensure your SSL is valid and your WordPress Address (URL) in settings begins with https://.
People Also Ask
Does Cloudflare automatically add security headers to WordPress? No, Cloudflare does not add HTTP security headers automatically. You must configure them manually either on your origin server (Apache/Nginx) or by creating Cloudflare Transform Rules/Cloudflare Workers to inject the headers at the edge.
Can security headers fix vulnerabilities in my WordPress plugins? They do not "fix" poor code, but they act as a profound mitigating layer. For example, if a plugin has a Cross-Site Scripting (XSS) vulnerability, a properly configured Content Security Policy (CSP) can prevent the attacker's malicious payload from executing or sending data to an external server.
Why does my WordPress site still get a 'B' grade after adding headers?
Usually, this is because your Content Security Policy uses 'unsafe-inline'. Strict security scanners will cap your grade at a 'B' if you allow inline scripts. In the WordPress ecosystem, achieving an 'A' often requires advanced custom coding to nonce or hash every inline script.
Frequently Asked Questions (FAQ)
1. What are HTTP Security Headers? They are directives passed between the server and the browser in the HTTP response. They tell the browser how to behave, restricting malicious actions like clickjacking, code injection, and insecure connections.
2. Does WordPress set security headers out of the box? No. A vanilla installation of WordPress sends virtually zero security headers, leaving the burden of implementation entirely on the site owner or host.
3. Which method is the fastest for performance? Adding headers via your web server (Nginx or Apache) is the most performant method. It completely bypasses PHP execution, ensuring the fastest possible Time to First Byte (TTFB).
4. Will adding security headers break my site?
Most headers (X-Frame-Options, X-Content-Type-Options) are entirely safe and will not break your site. However, an improperly configured Content Security Policy (CSP) or Strict-Transport-Security (HSTS) header can easily break functionality or lock users out.
5. What is the HSTS Preload list?
It is a hardcoded list of domains built into web browsers like Chrome and Firefox. If your domain is on this list, the browser will refuse to connect via HTTP, even on the very first visit. You must include the preload directive in your HSTS header to submit your site to the list.
6. Do I need a plugin to add headers?
No. In fact, it is better to avoid plugins if you have access to your server configuration files or child theme's functions.php.
7. How do I test my WordPress security headers?
You can use the native network tab in your browser's developer tools, command-line tools like curl -I, or online scanning tools like SecurityHeaders.com or the SecHead Scanner.
8. What is the difference between X-Frame-Options DENY and SAMEORIGIN?
DENY completely prevents your site from being loaded inside an iframe anywhere, even on your own domain. SAMEORIGIN allows your site to be framed, but only by pages on the exact same domain.
9. Is X-XSS-Protection still necessary?
No. Modern browsers have deprecated the X-XSS-Protection header because it introduced its own security vulnerabilities. It is highly recommended to use Content-Security-Policy instead.
10. How do I handle third-party tracking pixels with CSP?
You must explicitly whitelist their domains in your CSP. For example, to allow Google Analytics, you must add https://www.google-analytics.com to both your script-src and connect-src directives.
11. Can I set headers specifically for the WordPress Admin area?
Yes, if using functions.php, you can wrap your header code in if ( is_admin() ) or if ( ! is_admin() ) to apply different policies to the frontend and backend.
12. Does caching affect security headers? Yes. When a page is cached, the caching mechanism must store and replay the headers. If you add headers to WordPress via PHP, you must purge your page cache so the new headers are bundled with the cached static HTML.
13. What is Referrer-Policy and why does WordPress need it?
It controls how much information about your site's URL is sent to external sites when a user clicks a link. By setting it to strict-origin-when-cross-origin, you prevent sensitive query parameters (like password reset tokens sometimes found in bad plugin designs) from leaking to third-party analytics or external websites.
14. Should I use 'unsafe-inline' in my CSP?
In an ideal world, no. In the WordPress world, almost definitely yes. The sheer volume of plugins and themes relying on inline CSS and JS makes removing 'unsafe-inline' practically impossible without a massive engineering budget.
15. How often should I audit my security headers? You should audit your headers anytime you change hosting environments, implement a new CDN, or install a major new WordPress plugin that relies on external APIs or scripts.
SEO Metadata
- Meta Title: How to Add Security Headers to WordPress (Complete Guide)
- Meta Description: Learn how to add the 6 essential security headers to your WordPress site via .htaccess, Nginx, functions.php, or plugins. Protect against XSS and clickjacking.
- URL Slug: /blog/security-headers-wordpress
- Primary Keyword: WordPress Security Headers
- Secondary Keywords: .htaccess, WP Security Plugins, Functions.php Headers, Nginx WordPress security
Related Security Guides
Continue your journey into web security with these related, deep-dive articles from the SecHead team:
Related articles
Free tool
Check your own security headers
Instant grade, plain-language explanations, and a full remediation plan - no signup needed.
Scan your site now â