This post contains affiliate links. If you buy something through one of them, Veravix may earn a commission at no extra cost to you.
A security headers check reads the instructions your server sends along with every page: which connections must be encrypted, whether other sites may frame your pages, and how browsers should treat scripts and file types. When I fetched the home pages of 63 well-known sites on 20 October 2026, 60 sent an HTTPS-enforcement header, but only 14 sent a Content Security Policy that actually controls scripts. Before I changed anything, this site and its sister site each sent exactly one security-related header, and it was not a protection header.
This guide explains the six headers a checker looks at, shows what a default WordPress install and a real sample of busy sites send, and gives a safe order for adding them, with the commands to check your own site in under a minute.
What a security headers check actually looks at
Scanners and graders look for the same six response headers. Each one is a short instruction to the visitor’s browser, and each protects against one specific problem:
| Header | What it tells the browser | Sent by (of 63 sites) |
|---|---|---|
Strict-Transport-Security (HSTS) |
Only ever use HTTPS for this site, for the stated time | 60 |
X-Content-Type-Options |
Trust the declared file type, do not guess (the value is nosniff) |
48 |
X-Frame-Options or CSP frame-ancestors |
Whether other sites may show this page inside a frame (clickjacking protection) | 44 send at least one |
Content-Security-Policy (CSP) |
Which sources may load scripts, styles, images and frames | 35 send some CSP, 14 control scripts |
Referrer-Policy |
How much of the page address goes to other sites when a visitor clicks away | 21 |
Permissions-Policy |
Which browser features, such as camera or location, the page may use | 10 |
A missing header is not automatically a vulnerability, and a full set does not make a site secure. The headers close specific browser-side gaps. They do nothing about a vulnerable plugin or a weak password, which is where most WordPress compromises start.
How to check your own site in a minute
You do not need a scanner. Your browser or a command line shows the raw headers, and the raw headers are what a scanner grades anyway.
- Command line. On macOS or Linux run
curl -sI https://example.com/. On Windows usecurl.exe -sI https://example.com/. The-Iflag asks for the headers only. - Browser. Open developer tools, switch to the Network tab, reload, click the first request (the page itself) and read the Response Headers.
- Check more than one address. Run it on the home page, on
/wp-login.phpand on/wp-json/. As the next section shows, they do not send the same headers. - Online graders. They run the same request and turn the result into a letter. Treat the letter as a checklist. A site can score well by adding harmless headers while leaving the risky parts alone.
What WordPress and the host send by themselves
On 20 October 2026 I requested five addresses on veravix.com with curl, on a standard Hostinger plan with no header changes made by me:
- The home page, the feed and the theme folder: only
content-security-policy: upgrade-insecure-requestsplus the server name. No HSTS, no frame protection, no nosniff, no referrer policy. - The login page,
/wp-login.php:x-frame-options: SAMEORIGIN,content-security-policy: frame-ancestors 'self'andreferrer-policy: strict-origin-when-cross-origin. WordPress’s ownsend_frame_options_header()function sends the first two (the developer reference documents both), so WordPress protects its login screen against framing but not your public pages. - The REST endpoint,
/wp-json/:x-content-type-options: nosniff.
The upgrade-insecure-requests header on every response is easy to misread. It does not restrict anything. It only tells the browser to load any leftover http:// resources on the page over HTTPS. Many scanners count any Content-Security-Policy header as present, so a site can show a CSP tick while having no policy at all. Four of the 35 sites in my sample that sent a CSP sent nothing else in it.
The host layer matters. The same request also shows x-powered-by: PHP/8.3.35, which tells every visitor your PHP version. It is not one of the six headers, and 10 of the 63 sampled sites sent an x-powered-by header too. My guide to checking your WordPress PHP version shows where it comes from.
What 63 real sites sent
I requested the home page of 69 well-known domains with a plain GET request and followed redirects: online stores, hosting companies, WordPress and web-tooling sites, news and open-source projects, and software-as-a-service tools. Five sites that sit behind bot protection answered the script with a 403 and one did not resolve, so 63 are counted. The sample skews large and well-resourced, so read the numbers as what popular sites do, not as the average small-business site.
- HSTS is nearly universal and often too short. 60 of 63 send it. 36 set at least a year (31,536,000 seconds), the minimum hstspreload.org requires, and 25 include the
preloaddirective. 24 send less than a year. 11 of the online stores in the sample use exactly 7,889,238 seconds, about three months, which looks like a shared platform default. wordpress.org itself sentmax-age=3600, a single hour, and one software site sentmax-age=0, which tells browsers to forget the policy. - nosniff and clickjacking protection are the easy wins. 48 sites send
nosniffand 47 of those send it exactly once. 38 sendX-Frame-Options(20 SAMEORIGIN, 17 DENY, and one with the obsoleteALLOW-FROMvalue, which MDN says modern browsers ignore). Countingframe-ancestorsas well, 44 sites are covered. - A real CSP is rare. 35 send a CSP header. Only 14 include
default-srcorscript-src, the directives that control scripts. 8 of the 35 allow'unsafe-inline'and 6 allow'unsafe-eval', both of which weaken the protection. Another 7 sites sendContent-Security-Policy-Report-Only, the testing mode, instead of or beside it. - Referrer-Policy and Permissions-Policy are optional in practice. 21 send a referrer policy, and 12 of those use
strict-origin-when-cross-origin, which is what browsers apply anyway when nothing is set. 10 send a Permissions-Policy. - A deprecated header still goes out. 33 of 63 send
X-XSS-Protection. MDN marks it deprecated and non-standard, and warns that it can create vulnerabilities in otherwise safe sites. 26 of the 33 send1; mode=block, 4 send1, and 3 send0, the value MDN says avoids the problem. - Complete sets are rare. Only 4 sites sent all six headers. Two sent none of them. The average site sent 3.4 of the 6.
The pattern is clear. Sites turn on the headers that cannot break anything (HSTS, nosniff, framing) and skip the one that can: a CSP that actually restricts scripts. That is a sensible order, and it is the order the next section follows.
Which headers to add, safest first
Add one group at a time, load the site, check the pages that matter (home page, a post, search, any checkout or login), and then run curl again.
X-Content-Type-Options: nosniff. It almost never causes problems on a normal WordPress site and is the first one to add.Referrer-Policy: strict-origin-when-cross-origin. Per MDN this is the default in current browsers, so setting it makes the behaviour explicit. It sends only your origin to other sites and nothing at all on a downgrade to HTTP. Some analytics and affiliate dashboards want the full address; if one breaks, that is the first suspect.X-Frame-Options: SAMEORIGIN(orContent-Security-Policy: frame-ancestors 'self'). It stops other sites from framing yours. If you deliberately let a partner embed your pages, do not add it. It does not affect pages you embed from others, such as a payment form.Permissions-Policy. A restrictive start iscamera=(), microphone=(), geolocation=(), only if your pages use none of them.- HSTS, with care. Browsers remember it for the whole
max-age. MDN notes that after the header is received, a certificate error on that host cannot be clicked through, and thatmax-age=0only takes effect when a browser makes a secure request. Make sure every page and subdomain you use works over HTTPS first, and start with a shortmax-agesuch as a day, then raise it. AddincludeSubDomainsandpreloadonly if you understand that the preload list asks for at least a year, subdomains included. - CSP last, and in report-only mode first. A WordPress site loads scripts from your theme, plugins, an analytics tag and often an ad network, so a policy copied from another site will block your own scripts. Start with
Content-Security-Policy-Report-Only, read what it would have blocked, and tighten from there. 7 of the 63 sites in my sample were doing exactly that. - Do not add
X-XSS-Protection. If your host or plugin sends it,0is the safe value, per MDN.
Where to put the headers in WordPress
There are three routes, and which works depends on how your site is served.
1. The server file. On Apache-compatible hosting, which includes many LiteSpeed shared plans, add this to .htaccess above the WordPress block. Back the file up first, because a typo returns a 500 error for the whole site:
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
</IfModule>
2. A small PHP snippet in a code-snippets plugin, using WordPress’s send_headers hook:
add_action( 'send_headers', function () {
header( 'X-Content-Type-Options: nosniff' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
header( 'X-Frame-Options: SAMEORIGIN' );
} );
3. A headers plugin. It gives you a settings screen instead of code, and it usually uses the same hook as the snippet.
The catch with routes 2 and 3 is that a PHP hook only runs when WordPress runs. If a caching plugin serves pages as ready-made files, those pages may skip the hook, and the login page will show the header while the home page does not. That is why the earlier tip matters: after any change, run curl on the home page and on a post, not just the one you edited. Here is what happened when I tried route 2 myself. On 20 October 2026 I added those three lines (nosniff, the referrer policy and SAMEORIGIN) to this site and its sister site as a code snippet, and checked with curl. Two things surprised me.
- The snippet scope mattered. A snippet set to run on the front end only did nothing, because it loaded too late for the
send_headershook. The headers appeared only after I set the snippet to run everywhere. - Cached pages kept the old response. A page WordPress generated fresh, such as an address with a query string the cache does not store, carried all three headers. A page served from the cache did not. This host stacks LiteSpeed Cache and a content delivery network edge cache, and a page already stored in either is not rebuilt until it is purged or expires. The headers reach each page as its cached copy is replaced, so test a page you know was rebuilt, and clear the cache after adding headers.
The survey figures in this guide describe the headers as they were before that change.
What to do after you change anything
Purge your cache, run curl -sI on the home page, a post, the login page and the REST address, and compare the result with the table at the top. Load the site in a private window and open the browser console, where a blocked resource is reported. Then re-check after plugin updates and after any host migration, since both can change which headers go out. If you moved hosts recently, my guide to finding out who hosts a website explains why the headers on the new host may look different from the old one.
Checked for accuracy on 20 October 2026, and updated the same day with the result of adding three headers to this site. The survey fetched one GET request per site, with redirects followed, from the Netherlands on 20 October 2026, and counted the response headers as received. Headers can differ by page, by location and by time, and five bot-protected sites could not be counted, so another run can give slightly different numbers. Header behaviour was checked against MDN and the WordPress developer reference.

Etienne Basson works with website systems, SEO-driven site architecture, and technical implementation. He writes practical guides on building, structuring, and optimizing websites for long-term growth.