Why most sites score D or F
If you run securityheaders.com or our HTTP Security Headers Grader against the top 1,000 small-business websites, roughly 70% will score D or F. Not because the sites are catastrophically insecure — most run on perfectly modern stacks. They score badly because the headers protect against attacks that need explicit opt-in from the site owner, and most owners never opt in.
Six headers, configured correctly, take a typical site from "F" to "A". Most are one-line additions to your nginx or Apache config. None require code changes. None have downside for a normal website.
This article walks through each one with the exact directive to add and the exact thing each one defends against.

1. Strict-Transport-Security (HSTS) — the biggest grade lift
What it does: tells browsers to always use HTTPS for your domain, even if a user types `http://` or clicks an old http link. Once a browser sees the header, it remembers — typically for a year — and refuses to make any non-HTTPS request to your site.
Why this matters: without HSTS, a user on a hostile network (coffee shop wifi, hotel) who types `yoursite.com` into the address bar makes a plain HTTP request first. The server redirects to HTTPS — but the initial request is in cleartext, and an attacker can intercept it before the redirect lands.
The directive:
```
Strict-Transport-Security: max-age=31536000; includeSubDomains
```
In nginx:
```
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```
In Apache:
```
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
```
For Cloudflare-fronted sites: SSL/TLS → Edge Certificates → HSTS → enable.
Caveat: once you ship HSTS, browsers remember it. If you ever need to roll back to HTTP, you can't — the browser refuses. Test thoroughly on a subdomain first. Most sites should be on HTTPS-only anyway in 2026.
2. X-Content-Type-Options: nosniff — easy win, no downside
What it does: tells the browser to trust the `Content-Type` header you send and not "sniff" the file content to guess the type.
Why this matters: without this header, a browser might decide that a file you served as `text/plain` looks like JavaScript and execute it as JS. If an attacker can upload a "txt" file that the browser auto-executes, that's a stored XSS.
The directive:
```
X-Content-Type-Options: nosniff
```
This has zero downside. Set it on every response. The "but my CMS serves files with wrong Content-Type" excuse means your CMS is wrong; fix it.
3. X-Frame-Options: SAMEORIGIN — clickjacking defense
What it does: prevents your site from being loaded inside an `
Why this matters: clickjacking attacks load your site (e.g. your bank's login page) in an invisible iframe, overlay a fake UI on top of it, and trick the user into clicking through to the real underlying button.
The directive:
```
X-Frame-Options: SAMEORIGIN
```
This allows your own site to embed itself in iframes but blocks anyone else. If you have a legitimate iframe partner (an embed widget), use `Content-Security-Policy: frame-ancestors` instead, which supports a whitelist.
4. Referrer-Policy: strict-origin-when-cross-origin — sensible default
What it does: controls how much of the URL is sent in the `Referer` header when users click links from your site to other sites.
Why this matters: without setting this, your site leaks the full URL of every page a user was on to every external site they click to. Pages with query parameters (which often contain session IDs, search terms, or PII) leak that data to third parties. Analytics platforms see your private pages.
The directive:
```
Referrer-Policy: strict-origin-when-cross-origin
```
This sends the full URL to your own subdomains but only the bare origin (e.g. `https://example.com`) to external sites. Good default for nearly all sites.
5. Permissions-Policy — disable unused browser features
What it does: controls which browser features (camera, microphone, geolocation, USB, etc.) your site is allowed to use. Setting them to `()` disables them entirely.
Why this matters: even if your site doesn't use the camera, a malicious script injected via XSS could request camera access. Permissions-Policy disables the feature at the HTTP layer so the browser never even shows the prompt.
The directive:
```
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(), usb=()
```
For sites that DO use specific features (e.g. a mapping app uses geolocation), list them with `self` or specific origins:
```
Permissions-Policy: camera=(), microphone=(), geolocation=(self), payment=(), usb=()
```
6. Content-Security-Policy — the hardest, biggest defense
What it does: tells the browser which sources are allowed to load scripts, styles, images, fonts, and frames. It's the most powerful XSS defense available in 2026.
Why this matters: even with input validation and output escaping (which you should do anyway), CSP provides a defense-in-depth layer. If an attacker successfully injects `