DEV Community

Cover image for Security Headers Checklist: 6 HTTP Headers Every Site Should Send
head_of_david
head_of_david

Posted on Originally published at devkult.com

Security Headers Checklist: 6 HTTP Headers Every Site Should Send

Security headers are the cheapest hardening you'll ever ship: a few lines of server config, no code changes, and whole classes of attack — clickjacking, MIME sniffing, protocol downgrades, much of XSS — get harder or impossible. Here are the six worth sending on every site, in the order that's safest to roll them out.

The checklist

Header Recommended value Stops
X-Content-Type-Options nosniff Files being run as a type they weren't served as
Referrer-Policy strict-origin-when-cross-origin Full URLs leaking to other sites
X-Frame-Options DENY (or CSP frame-ancestors 'none') Clickjacking via iframes
Permissions-Policy camera=(), microphone=(), geolocation=() Injected code asking for powerful features
Strict-Transport-Security max-age=31536000; includeSubDomains HTTPS → HTTP downgrades
Content-Security-Policy depends on your site — start in report-only mode Most XSS

The first four are nearly risk-free: they rarely break anything. HSTS and CSP need care, so they come last. To build all six at once for your server or host, use the security headers generator.

1–4: the safe ones

X-Content-Type-Options: nosniff makes browsers trust your Content-Type instead of guessing. The only thing it can break is a script or stylesheet served with the wrong type, which you want to fix anyway.

Referrer-Policy: strict-origin-when-cross-origin is already the default in current browsers; sending it makes the behaviour explicit for older ones. Use no-referrer or same-origin if your URLs carry anything sensitive.

X-Frame-Options: DENY stops other sites from loading your pages in an iframe. Use SAMEORIGIN if you frame your own pages. Once you have a CSP, its frame-ancestors directive does the same job and is the modern replacement — send both for older browsers.

Permissions-Policy switches off browser features you don't use. List the ones that matter — camera, microphone, geolocation, payment — with an empty allowlist ().

5: HSTS, carefully

Strict-Transport-Security tells browsers to use HTTPS for your domain, every time, for max-age seconds — even if someone types http://. It's powerful precisely because it can't be undone quickly: if a subdomain doesn't support HTTPS and you've sent includeSubDomains, browsers will refuse to load it until the max-age runs out.

Roll it out in steps:

  1. max-age=300 — five minutes, to test.
  2. max-age=86400; includeSubDomains — once you've confirmed every subdomain serves HTTPS.
  3. max-age=31536000; includeSubDomains — a year, the usual production value.
  4. Add preload and submit the domain to hstspreload.org only if you're sure; removal takes months.

6: CSP, in report-only mode first

A Content-Security-Policy tells the browser which sources may provide scripts, styles, images and frames. A good one turns most injected-script XSS into a blocked request. It's also the header most likely to break your site, so start with Content-Security-Policy-Report-Only: the browser reports violations to the console (and to an endpoint, if you configure one) without blocking anything.

A starting policy for a site that serves its own assets:

Content-Security-Policy: default-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Enter fullscreen mode Exit fullscreen mode

Third-party scripts, analytics and inline <script> blocks all need explicit allowances — the CSP guide covers nonces, hashes and the usual breakages.

Setting them

Next.js (next.config.js):

const securityHeaders = [
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  { key: "X-Frame-Options", value: "DENY" },
  { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
  { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" },
];

module.exports = {
  async headers() {
    return [{ source: "/(.*)", headers: securityHeaders }];
  },
};
Enter fullscreen mode Exit fullscreen mode

nginx (inside the HTTPS server block — always adds them to error pages too):

add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header X-Frame-Options "DENY" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Enter fullscreen mode Exit fullscreen mode

One nginx trap: add_header directives are inherited only if the inner block defines none of its own. A single add_header inside a location silently drops every header set at the server level for that location.

Express: the Helmet middleware sets sensible defaults for all of these, including a starter CSP — app.use(helmet()).

Vercel (vercel.json) and Netlify (_headers):

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [{ "key": "X-Content-Type-Options", "value": "nosniff" }]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode
/*
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
Enter fullscreen mode Exit fullscreen mode

Headers you can drop

  • X-XSS-Protection — the browser XSS filters it controlled have been removed. Send 0 or nothing.
  • Public-Key-Pins (HPKP) — removed from browsers; it could lock users out of a site permanently.
  • Expect-CT — obsolete now that Certificate Transparency is enforced by default.
  • Feature-Policy — replaced by Permissions-Policy.

Check your work

curl -sI https://your-site.example | grep -iE "strict-transport|content-security|x-content-type|referrer-policy|permissions-policy|x-frame"
Enter fullscreen mode Exit fullscreen mode

To check many URLs at once, paste them into the HTTP Status Checker. For every header's full set of values, see the HTTP headers reference.


Originally published at devkult.com. To build all six for your server or host in one go, use the free Security Headers Generator.

Top comments (0)