← All guides

Security headers, explained by what our scanner sees

Five short lines in your server response stop most clickjacking, script injection and downgrade attacks. Most sites we scan send none of them. Here is what each one does, in plain terms, and how to add them on Apache, Nginx and Cloudflare.

16 September 2026, 3 min read. We added every one of these to our own site this week, and the scanner graded us before and after.

When a browser asks your server for a page, the server sends the page plus a set of headers, short lines of text the visitor never sees. Five of those lines tell the browser how to protect the visitor while they are on your site. Leave them out and the browser falls back to its most permissive behaviour.

Our scanner checks for all five. Across the sites people have run through it so far, the most common result is zero out of five. That includes sites with a padlock, a firewall plugin and a recent redesign. Headers are a server setting, and nobody sets them unless someone asks.

The five, in plain terms

Strict-Transport-Security, usually called HSTS. Tells the browser: for the next year, only ever talk to this site over HTTPS, even if someone types http. Without it, the very first request to your site can be intercepted on a public network before the redirect to HTTPS happens.

Strict-Transport-Security: max-age=31536000; includeSubDomains

Only send this once every part of your site works over HTTPS, because browsers remember it.

X-Content-Type-Options. Stops the browser guessing what a file is. Without it, an uploaded file that claims to be an image can be run as a script.

X-Content-Type-Options: nosniff

X-Frame-Options. Stops other sites loading your pages inside an invisible frame and tricking visitors into clicking things on it. That trick is called clickjacking, and it still works on most sites.

X-Frame-Options: SAMEORIGIN

Referrer-Policy. Controls how much of your address is passed along when a visitor clicks a link to another site. Without it, full addresses, including search terms or account pages, leak to whoever you link to.

Referrer-Policy: strict-origin-when-cross-origin

Content-Security-Policy, or CSP. The big one. It lists where scripts, styles, images and fonts are allowed to load from. Injected script from an unknown domain simply does not run. It is also the one that breaks things if written carelessly, because it will block your own analytics or fonts if you forget to list them.

A starting policy for a typical site that uses Google Fonts:

Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; frame-ancestors 'self'; base-uri 'self'; form-action 'self'

Yes, the ‘unsafe-inline’ part weakens it. A policy with that allowance is still far better than no policy, and it is where nearly every real site has to start. Tighten it later.

A sixth line, Permissions-Policy, turns off camera, microphone and location access for your pages. It costs nothing to add:

Permissions-Policy: camera=(), microphone=(), geolocation=()

How to add them

Apache, which is most shared hosting. In the .htaccess file at the root of the site:

<IfModule mod_headers.c>
  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"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
</IfModule>

Nginx. In the server block, then 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=31536000; includeSubDomains" always;

Cloudflare. Under Rules, open Transform Rules and add a response header rule with each line. It then applies to everything behind Cloudflare without touching the server. HSTS also has its own switch under SSL/TLS, Edge Certificates.

WordPress with no server access. A small plugin, or a few lines in a must-use plugin, can send them with PHP. It works, but the server or Cloudflare is the better place, because that also covers files WordPress never touches.

What happened on our own site

We built this site, ran it through our own scanner, and it failed the headers section. None of the five were set. It took twenty minutes to add them and check that nothing broke. The policy blocked one thing we had not expected: WordPress’s own emoji script, which starts a background worker the policy did not allow. We removed the script rather than loosen the policy.

Check yours. Paste the address into the scanner and the headers section shows exactly which of the five are missing.

See where your own site stands.

The scanner checks the things this guide talks about, in about ten seconds, no signup.

Tell us about the site.

A straight answer and a fixed quote, usually the same day.