Table of contents22
A few years ago I ran a client’s brand-new WordPress site through a free security scan, expecting a clean bill of health. The site had SSL, strong passwords, updated plugins — everything I had been taught to do. The scanner gave it a D. A big red D, right at the top of the report, next to the words “missing security headers.” I had never even heard of security headers. I remember staring at that grade thinking, how can a site be “secure” and still get a D? That question pulled me deep into WordPress security headers setup, and I have never looked back.
I’m Tom Baker, and I have spent the last decade setting up and securing WordPress sites for small businesses. That D grade sent me down a rabbit hole that changed how I think about WordPress security. Security headers turned out to be one of the highest-value, lowest-effort improvements you can make to a WordPress site — a few lines of configuration that block entire categories of attacks your firewall, your SSL certificate, and your security plugins simply do not cover.
Here is the fear worth naming: you can do everything “right” — updates, backups, strong passwords — and still leave your visitors exposed to clickjacking, content injection, and protocol downgrade attacks, because none of those defenses tell the visitor’s browser how to behave. Security headers are the instructions your server sends to every browser, and without them the browser just guesses.
In this practical guide to WordPress security headers setup, I will explain what each header actually does in plain language, show you exactly how to add them with a copy-paste .htaccess block, walk you through testing your site with free tools, and warn you about the common mistakes that break sites — including one famous header you should stop adding. This is for WordPress site owners who want a real, testable security improvement today, not theory. Ready? Let’s get started.
One honest note up front: the goal here is not a perfect score on day one. Some headers are safe to deploy in ten minutes with zero risk. One of them — Content Security Policy — can break your site if you rush it. I will show you the safe order: easy wins first, the tricky one last, with a safety net built in.

What You Will Learn in This Guide
- What security headers are, explained without jargon — and why your SSL certificate is not enough
- What each essential header does: X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, HSTS, and CSP
- The exact .htaccess block to add, plus the Nginx equivalent
- How to test your site with securityheaders.com and read your grade honestly
- The safe deployment order that protects your site without breaking it
- Common mistakes: the obsolete header to stop using, the HSTS trap, and the CSP breaking problem
Ready? Let’s get started.
What Security Headers Actually Are (No Jargon)
Every time someone visits your WordPress site, your server sends back two things: the page itself, and a set of invisible instructions called HTTP headers. You never see them. Your visitors never see them. But their browsers read every single one, and those instructions decide how cautiously the browser should treat your pages.
Think of it like this. Your SSL certificate is the armored truck that delivers the page safely. Security headers are the handling instructions taped to the package: “do not open this inside another frame,” “do not guess what kind of file this is,” “only accept scripts from these approved sources.” Without the instructions, the delivery still arrives — but the recipient handles it carelessly, and that carelessness is exactly what attackers exploit.
Here is what surprised me when I first learned this: your firewall protects your server, and your SSL protects data in transit, but neither one tells the visitor’s browser how to defend itself. Clickjacking, MIME-sniffing attacks, and malicious script injection all happen inside the visitor’s browser — the one place your server-side defenses cannot reach. Security headers are the only layer that reaches into the browser and says, “be careful with this page.” That is why they matter, and that is why no security plugin can fully replace them.
The Safe Four: Deploy These Today With Zero Risk
When I first added security headers to a client site, I made the classic beginner mistake: I added everything at once, including an aggressive Content Security Policy, and the site’s contact form stopped working. I spent an embarrassed afternoon undoing it. Learn from my mistake. There are four headers that are genuinely safe to deploy right now — they do not change how your site looks or behaves, they just remove attack surface. Start here.
X-Content-Type-Options: Stop the Browser From Guessing
Browsers sometimes try to be helpful by guessing what type of file they received — a behavior called MIME sniffing. If an attacker manages to upload a file that looks like an image but contains script code, a sniffing browser might execute it as a script. The X-Content-Type-Options header with the value nosniff tells the browser: trust the declared file type, never guess. One line, no downside, real protection against a sneaky class of upload attacks.
X-Frame-Options: Block Clickjacking
Clickjacking is one of those attacks that sounds theoretical until you see it. An attacker embeds your site inside an invisible frame on their malicious page, and tricks your visitors into clicking buttons on your site without knowing it — “click here to win a prize” secretly clicks “delete my account” underneath. The X-Frame-Options: SAMEORIGIN header tells browsers your pages may only be framed by pages from your own domain. Your site still works normally; it just cannot be embedded by strangers. Unless you specifically need other sites to frame your content, this header is pure win.
Referrer-Policy: Stop Leaking Visitor Data
When a visitor clicks a link on your site, the browser tells the destination site where the visitor came from — the “referrer.” By default, that can include the full page URL, which sometimes contains sensitive bits like password-reset tokens or private paths. Referrer-Policy: strict-origin-when-cross-origin tells the browser to send only your domain name (not the full URL) when navigating to other sites, while keeping full referrer info for your own internal navigation. Your analytics keep working. The data leakage stops.
Permissions-Policy: Lock the Camera, Mic, and Location
Modern browsers let websites request access to the camera, microphone, and geolocation. Most WordPress sites never need any of these. Permissions-Policy lets you switch them off explicitly — for example, camera=(), microphone=(), geolocation=() means “nobody is allowed to use these features on my site.” The empty parentheses are the point: they mean no one, not even your own pages. If a malicious script ever does get injected into your site, this header stops it from silently turning on a visitor’s camera. It costs you nothing.
These four headers do not change, do not need tuning, and once they are in place you can mostly forget about them. They are the paste-and-forget foundation. The next two headers are more powerful — and need more care.

HSTS: Force HTTPS, Every Time, No Exceptions
You probably already have an SSL certificate and your site loads over HTTPS. Good. But here is the gap most people miss: the very first time a visitor types your domain, their browser defaults to plain HTTP and your server redirects them to HTTPS. An attacker sitting on the same network can intercept that first insecure request — a classic man-in-the-middle attack called SSL stripping — and the visitor never knows.
Strict-Transport-Security, or HSTS, closes that gap. It tells the browser: “from now on, only ever talk to this site over HTTPS — never even try HTTP.” The header looks like this:
Strict-Transport-Security: max-age=31536000; includeSubDomains
The max-age value is in seconds — 31536000 is one year. The includeSubDomains part extends the rule to all your subdomains. Here is the critical warning I wish someone had given me: do not enable HSTS until you are certain your entire site works perfectly over HTTPS, including every subdomain. Once a browser receives this header, it will refuse plain-HTTP connections for the whole max-age period. If part of your site still needs HTTP, you will lock yourself and your visitors out with no easy undo. I always start with a short max-age like 86400 (one day) for the first week, verify everything works, and only then raise it to a year.
There is also a preload option that submits your domain to a list built into browsers themselves, so even the very first visit is HTTPS-only. That is the strongest version — and the hardest to undo, because removal requires a request to the preload list operators and months of waiting. Treat preload as a one-way door. Only walk through it when you are completely sure.
CSP: The Most Powerful Header (Handle With Care)
Content Security Policy is the heavyweight champion of security headers. Where the other headers each close one specific gap, CSP lets you write an allowlist of exactly where your site is permitted to load scripts, styles, images, and fonts from. If an attacker injects a malicious script into your page, the browser checks the CSP, sees the script’s source is not on the allowlist, and refuses to run it. It is the closest thing to a silver bullet that browser security has.
It is also the header that breaks WordPress sites, because WordPress and its plugins load scripts from many places — your theme, your plugins, Google Fonts, analytics, video embeds, ad networks. A strict CSP that only allows your own domain will silently break half of these. That is exactly what happened to my client’s contact form years ago, and it is why so many guides either skip CSP or give you a copy-paste policy that is so permissive it protects nothing.
The professional approach — the one I use on every site now — is to start in report-only mode. Instead of enforcing the policy, the browser just reports what it would have blocked. You watch your browser’s developer console for a few days, see which legitimate sources get flagged, add them to your allowlist, and only switch to enforcement when the reports go quiet. The header for the safe phase looks like this:
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' data: https:; frame-ancestors 'self'
Yes, those ‘unsafe-inline’ values look alarming, but WordPress core and most plugins rely on inline scripts and styles, so a policy without them will break things immediately. Report-only mode lets you tighten the policy gradually and honestly, instead of pretending a perfect policy works on day one. The goal is progress: even a moderately strict enforced CSP blocks the vast majority of injection attacks.

WordPress Security Headers Setup: The Practical Method
Now the hands-on part. You have three reasonable places to set these headers: your web server configuration, an edge layer like a CDN, or a plugin. Setting them at the server or edge level is the most reliable, because the headers then apply to every response consistently — including pages served from a full-page cache, which can bypass PHP-level headers entirely.
Method 1: .htaccess (Apache and LiteSpeed)
Most shared hosting runs Apache or LiteSpeed, where the .htaccess file in your WordPress root controls server behavior. Back up your .htaccess first — a syntax error here can take down your site just like a wp-config.php typo. Then add this block. I am deliberately leaving CSP in report-only mode and HSTS at a cautious one-day max-age for your first week:
<IfModule mod_headers.c>
# Prevent MIME sniffing
Header always set X-Content-Type-Options "nosniff"
# Block clickjacking
Header always set X-Frame-Options "SAMEORIGIN"
# Limit referrer leakage
Header always set Referrer-Policy "strict-origin-when-cross-origin"
# Disable unused browser features
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
# Force HTTPS (start with 1 day, raise to 31536000 after a clean week)
Header always set Strict-Transport-Security "max-age=86400; includeSubDomains"
# CSP in report-only mode while you tune it
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self' 'unsafe-inline' https:; style-src 'self' 'unsafe-inline' https:; img-src 'self' data: https:; font-src 'self' data: https:; frame-ancestors 'self'"
</IfModule>
The IfModule wrapper is important: it means these lines only run if Apache’s headers module is enabled, so a server without it will not throw errors. After saving, clear any caching plugin and visit your site to confirm everything still works.
Method 2: Nginx Server Block
Nginx ignores .htaccess completely, so if your server runs Nginx you add the headers to your site’s server block instead. One Nginx-specific gotcha: the always parameter matters — without it, headers can silently disappear on error responses. After editing, test the configuration and reload:
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=86400; includeSubDomains" always;
Method 3: At the Edge (CDN)
If your site sits behind a CDN, you can set headers at the edge — for example with transform rules. The advantage is that edge headers survive origin server changes, migrations, and caching plugin updates that might otherwise wipe out your .htaccess work. Many site owners do both: server-level headers as the foundation, edge rules as belt and suspenders. Just be careful not to set the same header in two places with conflicting values — I will explain why in the mistakes section.

How to Test Your Headers (And Read Your Grade Honestly)
Adding headers without testing is like installing a lock without checking it clicks. The good news is testing is free and takes thirty seconds. Go to securityheaders.com, enter your domain, and scan. You will get a letter grade from F to A+.
Most WordPress sites start at D or F — that was my client’s site, and it is probably yours too before this guide. After adding the safe four plus HSTS, expect to jump to a B or A. A full A+ usually requires an enforced CSP, which is a longer project. Here is the honest read: the grade is a guide, not the goal. A site with a B grade and a carefully tuned report-only CSP is genuinely safer than a site with an A+ from a copy-pasted policy the owner does not understand. For a deeper analysis, Mozilla Observatory gives you a detailed breakdown of each header with explanations.
Test more than your homepage. A complete WordPress security headers setup applies to every page, including posts, category archives, and any cached pages. If your homepage scores well but inner pages do not, your caching layer is probably serving pages without the headers — which is exactly why server-level or edge-level configuration beats PHP-level code for this job.
Common Mistakes That Break Sites (Learn From Mine)
I have made most of these mistakes myself, so consider this the section I wish someone had handed me on day one.
Mistake 1: Adding X-XSS-Protection
You will still see X-XSS-Protection recommended in older tutorials and even some automated site-health suggestions. Here is the truth: it is a relic. Chrome removed support for it years ago, Firefox never supported it at all, and security researchers have shown it can actually create vulnerabilities in otherwise safe sites in certain edge cases. Sending it does not help modern browsers one bit. If a guide tells you it is essential protection, that guide is outdated. Skip it.
Mistake 2: Enabling HSTS With Preload Too Early
HSTS with a long max-age and preload is effectively a one-way door. I once enabled full preload on a staging subdomain setup before confirming every subdomain had valid SSL, and visitors got hard browser errors with no bypass button. Start with a short max-age, verify for a full week, raise it gradually, and treat preload as a decision you make once a year — not on day one.
Mistake 3: Enforcing CSP Before Tuning It
Jumping straight to an enforced Content-Security-Policy is the number one way headers break WordPress sites. Contact forms, video embeds, analytics, fonts — things stop working silently, and the breakage looks like a plugin bug rather than a header problem. Always run report-only mode first, watch the console, whitelist what is legitimate, and enforce only when the reports go quiet. Patience here saves you a debugging nightmare.
Mistake 4: Setting the Same Header Twice
If your CDN sets X-Frame-Options and your .htaccess sets it too, browsers can receive the header twice with conflicting values — and conflicting duplicate headers cause unpredictable behavior. Pick one layer as the source of truth for headers. If your host already sets a baseline of security headers at the server level (many managed hosts do), check what is already there before adding your own.
Mistake 5: Setting Headers Only in PHP
You can add headers with a few lines in your theme’s functions.php, and plenty of tutorials show you how. It works — until a caching plugin serves a cached page without running PHP, or a static file gets requested directly. Server-level headers apply to every response, including cached pages and static files. Use PHP-level headers only for conditional logic that truly needs WordPress context, and keep the baseline at the server or edge.
Headers are one layer of a complete security posture. Pair them with the fundamentals: locked-down file permissions, brute-force protection on your login page, and a tested backup routine. If you have not hardened your configuration files yet, our safe WordPress update checklist is a good companion read.
WordPress Security Headers Setup: Frequently Asked Questions
Will security headers slow down my site? No. Headers add a few dozen bytes to each HTTP response — completely negligible. If anything, HSTS can make repeat visits marginally faster by skipping the HTTP-to-HTTPS redirect. There is no performance reason to avoid them.
Can security headers break my WordPress site? The safe four (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy) will not break anything on a normal site. HSTS can lock you out of HTTP if enabled prematurely, and an enforced CSP can break plugins that load external scripts. Follow the safe deployment order in this guide — easy headers first, HSTS cautiously, CSP in report-only mode — and you will be fine.
Do I still need an SSL certificate if I have HSTS? Yes — HSTS depends on SSL. HSTS does not encrypt anything itself; it just tells browsers to always use the encrypted HTTPS connection your certificate provides. Without a valid certificate, HSTS cannot work and will cause errors. Get SSL working perfectly first, then add HSTS.
What is a good score on securityheaders.com? Most WordPress sites start at D or F. The safe four plus HSTS typically gets you to B or A. A+ generally requires an enforced Content Security Policy. Treat the grade as a progress indicator: moving from F to B closes the most important gaps, and the remaining climb is the longer CSP tuning project.
Should I use a plugin to manage security headers instead? You can, but you do not need to — and for this particular job, server-level configuration is more reliable because it covers cached pages and static files. The no-paid-plugins philosophy applies here: a few lines in .htaccess do the job permanently, with no plugin to update, no settings to migrate, and no code running on every request.
Final Thoughts
That D grade on my client’s scan bothered me for weeks, but looking back I am grateful for it. It taught me that WordPress security has layers most of us never see, and that some of the most valuable improvements are also the simplest. A few lines in .htaccess — lines you can add in ten minutes — put instructions into every visitor’s browser that block clickjacking, MIME-sniffing, data leakage, and protocol downgrade attacks. No plugin, no subscription, no ongoing maintenance.
You now know what each header does, you have the exact configuration to paste, you know how to test your grade, and you know the mistakes to avoid — including the obsolete header to stop adding and the one-way door of HSTS preload. Start with the safe four today. Add HSTS carefully this week. Begin your CSP report-only tuning and let it run in the background.
The honest line: headers will not save a site with weak passwords and outdated plugins — they are one layer, not the whole wall. But they are the layer that protects your visitors inside their own browsers, where nothing else you install can reach. That alone makes them worth ten minutes of your time. For the full picture, keep our hacked site recovery guide bookmarked — hopefully you will never need it.
What grade did your site get before and after? Run the scan and tell me in the comments — I read every one, and I want to hear about your jump from red to green.
Published: October 18, 2026 · Updated: October 18, 2026 · By Tom Baker










