Security headers on WordPress

Clear the header findings on a WordPress site at the web server. Set security headers in .htaccess on Apache or in Nginx once you know what your host allows.

On Apache, the Header directive of mod_headers sets response headers. It can go in an .htaccess file when the server allows FileInfo overrides there. Write it as Header always set. With always the header also goes out on error responses and stays across internal redirects. Keep your own lines outside the # BEGIN WordPress and # END WordPress markers in .htaccess, because WordPress can overwrite anything between them. On Nginx, add_header sets a response header in an http, server or location block. Add the always parameter so the header goes out with every status code, not only with the success and redirect codes Nginx lists. A block that has any add_header of its own does not inherit the ones above it, so repeat the whole set in that block.

Sources: Apache HTTP Server: mod_headers, WordPress developer docs: Hardening WordPress and nginx: ngx_http_headers_module

Which of these files you can edit depends on your host. If your host manages the server for you, check its panel and documentation for a headers setting first.

Guardr has a WordPress plugin that shows the site's health grade, uptime percentage and response time in wp-admin. Read about the plugin.

Strict-Transport-Security (HSTS)

On Apache, in .htaccess:

.htaccess

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

On Nginx, in the server block:

nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Add includeSubDomains only when every subdomain answers over HTTPS.

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

Content-Security-Policy (CSP)

Start with the report-only header. On Apache, in .htaccess:

.htaccess

Header always set Content-Security-Policy-Report-Only "default-src 'self'"

On Nginx, in the server block:

nginx

add_header Content-Security-Policy-Report-Only "default-src 'self'" always;

Once the reports show the policy breaks nothing, rename the header to Content-Security-Policy to enforce it. On Apache:

.htaccess

Header always set Content-Security-Policy "default-src 'self'"

On Nginx:

nginx

add_header Content-Security-Policy "default-src 'self'" always;

Report-only mode shows what a policy would stop without stopping it. The CSP fix guide linked below builds a working policy that way before it is enforced.

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

X-Frame-Options

On Apache, in .htaccess:

.htaccess

Header always set X-Frame-Options "SAMEORIGIN"

On Nginx, in the server block:

nginx

add_header X-Frame-Options "SAMEORIGIN" always;

SAMEORIGIN lets only your own pages frame the site. DENY refuses every frame.

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

X-Content-Type-Options

On Apache, in .htaccess:

.htaccess

Header always set X-Content-Type-Options "nosniff"

On Nginx, in the server block:

nginx

add_header X-Content-Type-Options "nosniff" always;

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

Referrer-Policy

On Apache, in .htaccess:

.htaccess

Header always set Referrer-Policy "strict-origin-when-cross-origin"

On Nginx, in the server block:

nginx

add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

Permissions-Policy

On Apache, in .htaccess:

.htaccess

Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

On Nginx, in the server block:

nginx

add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

Each feature with an empty list () is switched off for every origin. Name the features the site does not use.

Sources: Apache HTTP Server: mod_headers and nginx: ngx_http_headers_module

How to check it

After the change is live, read the headers the site sends with curl -I https://www.example.com/ or in the network panel of your browser developer tools. The free check gives an A to F grade and shows how to fix the first two findings in a few seconds, with no sign-up. Run the free check.

More on each header: how to fix a missing HSTS header, how to fix a missing Content-Security-Policy header, how to fix a missing X-Frame-Options header and the security headers guide.

The same headers on other platforms: Vercel, Netlify and Webflow.