The WordPress security headers the checker reads
Five response headers and HTTPS itself. Set them once at the server or CDN and every page carries them.
Written by Hamza Ahmad Aslam. Updated . One of the security headers guides.
What the checker sees
The headers on the final response for the page. Plain HTTP is a problem. A missing Strict-Transport-Security, X-Content-Type-Options or framing protection is a warning. A missing Referrer-Policy or Content-Security-Policy is a note, because both need more care to add. Framing protection counts as present with either X-Frame-Options or a CSP containing frame-ancestors.
The checker reads whether each header is there, not how strict it is. A weak policy passes.
What each one does
Strict-Transport-Securitytells browsers to use HTTPS for the site from now on, so a typedhttp://address never leaves the browser unencrypted.X-Content-Type-Options: nosniffstops a browser treating an uploaded file as a script because its contents look like one.X-Frame-Options: SAMEORIGINstops other sites loading yours in a frame to trick clicks.Referrer-Policy: strict-origin-when-cross-originsends only your domain, not the full address, when a visitor follows a link away.Content-Security-Policylists where scripts, styles and frames may come from. It is the strongest of the five and the easiest to get wrong.
Set them at the server
The server or CDN is the best place: the headers then cover images, feeds and error pages as well as the pages WordPress builds. For nginx, in the server block:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
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;One nginx catch: a location block with any add_header of its own drops every header set at the server level. Repeat them there, or use an include file.
For Apache with mod_headers:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin"From WordPress, when you cannot touch the server
<?php
add_action( 'send_headers', function () {
header( 'X-Content-Type-Options: nosniff' );
header( 'X-Frame-Options: SAMEORIGIN' );
header( 'Referrer-Policy: strict-origin-when-cross-origin' );
} );This only covers pages WordPress renders, and a page cache serves whatever headers it stored, so purge it after the change.
Two cautions
HSTS with includeSubDomains forces HTTPS on every subdomain, including old ones without a certificate. Check them first, and start with a short max-age such as 300 before moving to a year.
A strict Content-Security-Policy breaks most WordPress sites, because themes and plugins rely on inline scripts. A safe first policy that still counts is frame-ancestors 'self'; object-src 'none'; base-uri 'self'; upgrade-insecure-requests. Tighten it later with Content-Security-Policy-Report-Only to see what would break.
Check the fix
curl -sI https://example.com/ | grep -iE 'strict-transport|x-content-type|x-frame|referrer-policy|content-security'Check a site for this
Other fixes
- Hide the WordPress version numberThe version shows in the page's generator tag and in the stock readme.html. Both can go in a few minutes.
- Stop the WordPress REST API listing user namesThe users endpoint answers anonymous requests with account slugs, which are usually the login names.
- Turn off XML-RPC in WordPressxmlrpc.php still answers. Unless Jetpack or a publishing app needs it, block it at the server.