What security headers should I set?

Direct answer

Five worth setting: `Strict-Transport-Security`, `Content-Security-Policy`, `X-Content-Type-Options: nosniff`, `Referrer-Policy` and `Permissions-Policy`. Four take a minute. CSP takes real effort on an existing site and is the only one that meaningfully limits damage when something else goes wrong.

Muhammad HasanUpdated

Headers are defence in depth. None fixes a vulnerability; they limit what an attacker can do with one.

The quick ones

Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

HSTS tells the browser to use HTTPS for this domain from now on, closing the gap where a user types the address and the first request goes over HTTP. Only set it once HTTPS works everywhere, it's sticky, and a broken certificate becomes an outage rather than a warning. Start with a short max-age and raise it.

nosniff stops the browser guessing content types. Without it, a file you serve as text can be interpreted as JavaScript.

Referrer-Policy controls how much of your URL is sent to other sites. The default leaks full paths, which matters if your URLs contain identifiers or tokens.

Permissions-Policy disables browser features you don't use. Low value on its own, no cost.

X-Frame-Options and clickjacking

X-Frame-Options: DENY prevents your site being framed. It's superseded by CSP's frame-ancestors, but harmless to set both while older browsers exist.

Content-Security-Policy

The one that does real work, and the one that takes effort.

CSP restricts where scripts, styles, images and connections can come from. A well-configured policy means an injected script tag doesn't execute, because the source isn't allowed.

Content-Security-Policy: default-src 'self'; script-src 'self'; frame-ancestors 'none'

That's the ideal and it will break most existing sites immediately. Inline scripts, inline styles, third-party analytics, embedded widgets all stop working.

The workable approach is incremental:

  1. Deploy Content-Security-Policy-Report-Only with a report endpoint. Nothing breaks; you collect violations

  2. Read what's actually loading. It'll be more than you expected

  3. Tighten until the reports are only things you want to block

  4. Switch to enforcing

Avoid unsafe-inline in script-src, it removes most of the value. Use nonces or hashes for inline scripts you genuinely need.

Frameworks that inject inline styles make style-src harder. unsafe-inline for styles is a weaker compromise than for scripts, and often the pragmatic landing point.

Cookies

Not headers exactly, but the same layer:

Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax

HttpOnly keeps JavaScript from reading the cookie, so an XSS doesn't immediately become a stolen session. SameSite=Lax handles most CSRF.

Where to set them

Once, centrally. Reverse proxy, CDN, or framework middleware. helmet for Express, SECURE_* settings for Django, next.config.js headers for Next.js.

Not per-route. Anything applied per-route gets missed on the route added next month.

Checking

Load your site and read the response headers in the network tab. Several free scanners will grade the configuration for you, and a header check belongs in CI so a proxy change doesn't quietly remove them.

Go deeper

Kolega for DevOps

Related answers

See what your own repository returns

Connect a repo and run a scan. No credit card, no pipeline changes.

Get started for free