Most WordPress security advice focuses on plugins, passwords, and keeping core updated. Security headers rarely get mentioned. That is a gap worth closing, because a missing or misconfigured header can expose your visitors to cross-site scripting, clickjacking, and data leaks that no plugin will catch.
This guide covers the six headers that matter most for WordPress sites. For each one, you will see what it does, why it matters in a WordPress context, how to add it in both Apache and nginx, and how to verify it is working. You can also run a free scan at wpvanguard.com to see which headers your site is currently missing.
WordPress runs on PHP and outputs HTML. By default, the server sends no special security instructions to the browser. That means the browser trusts everything it receives from your domain, including scripts injected via an XSS vulnerability, iframes embedded on a phishing page, or MIME-type tricks used in file upload attacks.
Security headers are HTTP response headers your server sends alongside every page. They tell the browser exactly what content is allowed, how your site should be framed, what protocols to use, and what information to share with third parties. They cost nothing to add and require no plugin. A few lines in your server config can meaningfully reduce attack surface.
A missing Content-Security-Policy header means an injected script can run with full trust. No WAF catches what the browser willingly executes.
| Header | What It Prevents | Required? |
|---|---|---|
| X-Frame-Options | Clickjacking | Yes |
| Strict-Transport-Security | Protocol downgrade, MITM | Yes (HTTPS only) |
| X-Content-Type-Options | MIME sniffing attacks | Yes |
| Content-Security-Policy | XSS, data injection | Yes |
| Permissions-Policy | Feature abuse (camera, mic, geo) | Recommended |
| Referrer-Policy | Data leakage via Referer | Recommended |
This header controls whether your pages can be embedded in an iframe on another domain. Without it, an attacker can load your login page inside a transparent iframe overlay on their site. The visitor sees what appears to be a legitimate page and clicks what they think is a button – but their clicks land on your WordPress login form beneath it. Credentials get captured. This is clickjacking.
The Fix
Set this header to DENY if you never need your pages framed, or SAMEORIGIN if you embed your own pages in iframes (for example, a page builder preview).
Note: Modern browsers now use Content-Security-Policy’s frame-ancestors directive for the same purpose. X-Frame-Options still matters for older browsers and should be included alongside CSP.
You have an SSL certificate. Your site redirects HTTP to HTTPS. But there is still a window of vulnerability on the very first visit – the browser initially connects over HTTP before the redirect fires. An attacker positioned between the user and your server (a public WiFi scenario, for example) can intercept that first request.
HSTS closes that window. It tells the browser: for this domain, always use HTTPS – even before making a request. The browser caches this instruction and enforces it locally. No unprotected first request. No opportunity to intercept.
The Fix
Only add HSTS when your HTTPS setup is fully working. A misconfiguration here (setting HSTS when your cert has issues) will make your site completely inaccessible until the max-age window expires.
The includeSubDomains flag extends HSTS to all subdomains. Only add it if all your subdomains also run on valid HTTPS. The preload flag submits your domain to browser preload lists – once submitted, the instruction is baked into the browser itself. This is very hard to reverse, so treat it as a long-term commitment.
Browsers can try to detect file types by sniffing content regardless of the declared MIME type. A file uploaded as a JPEG that actually contains a JavaScript payload can be run by some browsers if they sniff the content and decide it looks like a script. This is MIME confusion attack territory, and it is especially relevant on WordPress sites where users can upload files.
This header is the simplest of the six. One option: nosniff. It tells the browser to trust the declared Content-Type and never sniff.
The Fix
CSP is the most powerful header on this list and the most difficult to configure correctly for WordPress. It defines exactly which sources of scripts, styles, images, fonts, and other resources are allowed to load on your pages. Anything not explicitly allowlisted gets blocked. If you work with plugins that use AI or external script integrations, it is also worth understanding how prompt injection attacks can leverage untrusted script execution – CSP is one of the few headers that can limit their blast radius.
For WordPress, the challenge is that core, plugins, and themes all load resources from various places – Google Fonts, CDNs, third-party analytics scripts, payment processors. A strict CSP that is not tuned to your specific site will break things. The right approach is to start permissive and tighten over time.
Why WordPress Makes CSP Hard
- Gutenberg uses inline styles and dynamic script execution in some contexts, which strict CSP blocks
- Plugin scripts often load from external CDNs
- Google Fonts, reCAPTCHA, and analytics scripts all need explicit allowances
- Page builders generate inline styles that conflict with
style-src 'self'
The Fix – Start With Report-Only Mode
Use Content-Security-Policy-Report-Only first. This header enforces nothing – it only reports violations to a URL you specify. Run it for a week, check the violation reports, and use that data to build your actual policy.
Once you have tuned the policy based on real violation reports, switch from Content-Security-Policy-Report-Only to Content-Security-Policy. For the WordPress admin area, you will typically need to allow inline scripts and styles. Restrict those more aggressively only on the public frontend where you have full control over which scripts load.
Start with Report-Only mode. A CSP that breaks your admin is worse than no CSP at all.
This header controls which browser APIs and features your pages are allowed to use – and which third-party scripts embedded on your pages are allowed to access. Without it, a third-party analytics or ad script could theoretically request access to the user’s camera, microphone, or geolocation.
For most WordPress sites, you do not need any of these features on the public frontend. Restricting them is simple and low-risk.
The Fix
Adjust the list based on your actual use. If you have a WooCommerce store that uses geolocation for tax calculation, keep geolocation=(self) instead of geolocation=(). If you embed videos with autoplay, keep autoplay=(self).
When a user follows a link from your site to an external page, the browser sends a Referer header containing the URL they came from. On most pages, this is harmless. But it becomes a problem when URLs contain sensitive data – password reset tokens, order confirmation IDs, user-specific query parameters.
Consider a WooCommerce order confirmation page at yoursite.com/checkout/order-received/12345/?key=abc123xyz. If that page has Google Analytics, social sharing buttons, or any external asset, the Referer header sent to those third parties exposes the order key.
The Fix
strict-origin-when-cross-origin is the recommended middle ground. It sends the full URL as Referer for same-origin requests (useful for your own analytics), but only the origin (domain, no path or query string) for cross-origin requests. Sensitive parameters never leave your domain.
Before adding headers, it is worth checking what your site currently sends. You can do this two ways.
Option 1: WP Vanguard Free Scanner
The WP Vanguard scanner runs a surface scan of any WordPress site for free. It checks security headers, outdated plugins and themes, SSL configuration, and known vulnerabilities. For security headers specifically, it shows which of the six headers are present, which are missing, and flags any that are misconfigured.
Enter your domain at wpvanguard.com – no login required. You get results in under a minute.
Option 2: Browser DevTools
Open Chrome DevTools, go to the Network tab, reload your homepage, click the first request, and check the Response Headers section. You will see every header your server is sending. Look for the six headers covered in this guide.
You can also check from the command line using curl:
Where you add these headers depends on your hosting setup. Managed hosts (WP Engine, Kinsta, Cloudways) typically use nginx. Shared hosts (SiteGround, Bluehost on legacy plans) usually use Apache with .htaccess. Check with your host if you are unsure.
Apache (.htaccess)
Add the following block to your .htaccess file, above the WordPress permalink block. Make sure the mod_headers module is enabled on your server.
Nginx (server block)
Add these directives inside your server block, before the PHP location block. If you are on a managed host, you may need to add these via a custom nginx config file rather than editing the main config directly.
After saving changes, test your nginx config with nginx -t before reloading. A syntax error in the server block will take down your site.
Several WordPress plugins add security headers via PHP, using header() calls in WordPress hooks. This works, but it is not the preferred approach for a few reasons.
- Headers set via PHP fire later in the request lifecycle than server-level headers
- If PHP crashes or WordPress fails to load, the headers never get sent
- A plugin adds a dependency – if it deactivates or conflicts, your headers disappear
- Server-level headers apply to all requests including static files, PHP errors, and 404 pages
That said, if you do not have server access (shared hosting with no .htaccess control, some managed hosts that lock the config), a plugin is a reasonable fallback. The HTTP Headers plugin and Headers Security Advanced and HSTS WP are commonly used options. Check the headers are actually being sent after installing – some plugins silently fail on managed hosts that override headers at the server level.
After adding your headers, run these checks before considering the work done.
- Check headers are sending: Use the curl command above or open DevTools and verify all six headers appear in response headers.
- Test frontend functionality: Load your homepage, a single post, and the WooCommerce checkout (if applicable). Look for console errors – CSP violations show up there.
- Test wp-admin: Log into the dashboard and check that the block editor, plugin installs, and theme customizer all work. CSP issues often surface here first.
- Re-run the WP Vanguard scan: Visit wpvanguard.com and confirm all six headers now show as present.
- Check with SecurityHeaders.com: This is an independent grading tool. Aim for a B or higher. An A requires a strict CSP with no inline script allowances, which many WordPress sites cannot achieve without significant custom work.
- Setting HSTS before SSL is stable: If your cert expires or has errors, HSTS will make your site completely inaccessible until the max-age window expires. Set HSTS last, after everything else is confirmed working.
- Using X-XSS-Protection: This header is deprecated and removed from modern browsers. The old
X-XSS-Protection: 1; mode=blockrecommendation is now considered harmful in some browsers. Do not include it. - Too-strict CSP without testing: A CSP policy applied without testing will break your admin, your plugins, or both. Always start with Report-Only mode.
- Forgetting multisite: On WordPress multisite, headers set at the server level apply to all subsites. Make sure your CSP policy accounts for any subsite that loads resources from different domains.
- Not checking Permissions-Policy for WooCommerce: If you use geolocation for tax, payment methods that need the clipboard API, or embedded maps, you may need to explicitly allow those features. Test your checkout thoroughly.
Security headers are one layer. They address browser-level attacks – clickjacking, XSS, protocol downgrade, MIME confusion. They do not address plugin vulnerabilities, weak credentials, server misconfigurations, or malware. For a full picture, see the WordPress Security Hardening Checklist which covers the complete security stack from server to application layer.
The WP Vanguard scanner checks headers alongside plugin vulnerability data, SSL configuration, and other surface-level indicators. Running it periodically – not just once – helps you catch configuration regressions, like headers that disappear after a server migration or a HSTS config that got stripped during a host change. You can read more about what WP Vanguard checks on the WP Vanguard blog.
Security headers are a low-effort, high-impact addition to any WordPress site. They require no plugin, no monthly cost, and no ongoing maintenance once configured correctly. The six headers covered in this guide address the most common browser-level attack vectors: clickjacking (X-Frame-Options), protocol downgrade (HSTS), MIME confusion (X-Content-Type-Options), script injection (CSP), feature abuse (Permissions-Policy), and data leakage (Referrer-Policy).
Start by checking what your site currently sends. Run a free scan at wpvanguard.com to get a baseline. Then work through the fixes above, starting with the easy ones (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) and building toward CSP as you understand what your site actually loads.
Check Your Site Now
Not sure which security headers your site is missing? The WP Vanguard free scanner checks all six headers plus plugin vulnerabilities, SSL configuration, and more. No signup required.
Content Security Policy HSTS HTTP headers security headers WordPress hardening
Last modified: March 3, 2026









