Security headers: how to add them to your site
HSTS, CSP, X-Frame-Options, X-Content-Type-Options and Referrer-Policy: what they do and how to add them with Cloudflare, Apache or Nginx.
What are these headers for?
Security headers are instructions the server sends to the browser with every page. Redirect Audit checks that five of them are present on the final page:
- Strict-Transport-Security (HSTS): the browser will always use HTTPS for your site, even if someone types
http://. - Content-Security-Policy (CSP): limits the resources the page may load, or which sites may display it in a frame.
- X-Frame-Options: stops another site from showing yours in an invisible frame (clickjacking). CSP’s
frame-ancestorsdirective does the same job. - X-Content-Type-Options: stops the browser from guessing a file’s type.
- Referrer-Policy: limits the referrer information sent to other sites.
They do not replace updates or well-configured hosting, but they reduce the impact of some attacks. Their presence does not guarantee they are well tuned: Redirect Audit only checks that they exist.
Safe starting values
These values suit most sites and do not block content:
Strict-Transport-Security: max-age=31536000
Content-Security-Policy: frame-ancestors 'self'; base-uri 'self'; object-src 'none'
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
HSTS, carefully: once received, the browser will refuse the site over HTTP for the whole max-age. Start with a short duration (max-age=300, 5 minutes), check that everything works over HTTPS, then move to one day, then one year. Only add includeSubDomains if all your subdomains work over HTTPS, and preload only if you are sure to keep HTTPS forever: that choice is very hard to undo.
CSP: the policy above protects against framing and a few abuses, but not against script injection. A full policy (script-src, style-src, etc.) depends on each site: test it first with the Content-Security-Policy-Report-Only header, which reports what would be blocked without blocking anything, before enforcing it.
Cloudflare
HSTS: in SSL/TLS → Edge Certificates, HSTS section, click Enable HSTS, read the warning and pick a duration (Max Age). The same page offers the No-Sniff Header option, which adds X-Content-Type-Options: nosniff.
The other headers: in Rules → Overview, create a Response Header Transform Rule that applies to all requests, with the Set static operation for each header:
Content-Security-Policy=frame-ancestors 'self'; base-uri 'self'; object-src 'none'X-Frame-Options=SAMEORIGINX-Content-Type-Options=nosniff(unless the No-Sniff option is already on)Referrer-Policy=strict-origin-when-cross-origin
Note: the Managed Transforms → Add security headers shortcut also adds two obsolete headers (X-XSS-Protection and Expect-CT), but neither HSTS nor CSP. A custom rule gives a cleaner result.
Apache and LiteSpeed
With the mod_headers module, add this to the HTTPS VirtualHost:
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000"
Header always set Content-Security-Policy "frame-ancestors 'self'; base-uri 'self'; object-src 'none'"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
- Keep the word
always: without it, Apache does not send these headers on redirects or error pages. - The same block works in an
.htaccessfile (often the only access on shared hosting or LiteSpeed), but it then does not apply to redirects defined in the server configuration. If you have the choice, prefer the VirtualHost.
Nginx
In the HTTPS server block:
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Content-Security-Policy "frame-ancestors 'self'; base-uri 'self'; object-src 'none'" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
- The word
alwaysalso adds them to error pages (404, 500…). - Common trap: a
locationblock with its ownadd_header(for exampleCache-Control) no longer inherits any header from theserverblock. Put the five lines in a file, for examplesecurity-headers.conf, and addinclude security-headers.conf;to theserverblock and to everylocationthat has its ownadd_header. Since Nginx 1.29.3, theadd_header_inherit merge;directive in thelocationblock also solves this.
Run nginx -t, then reload Nginx.
WordPress
WordPress has no setting for these headers. Add them at the server or Cloudflare level instead, as above. Some security or cache plugins can also add them: avoid defining them in two places with different values.
Check the result
Check your domain again in Redirect Audit: the “Security headers” panel should show 5/5. For a deeper look at the values, also use securityheaders.com. Finally, browse a few pages: if something no longer displays, the CSP usually needs adjusting.
These examples are a starting point: adapt them to your setup and test them before applying them to a live site.