Blog
4 min read

HTTP Security Headers Explained: HSTS, CSP, and the Rest (With a Copy-Paste Setup)

A practical guide to the HTTP security headers worth setting: Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, frame protection, Referrer-Policy and Permissions-Policy — what each prevents, safe values, a Next.js and Nginx config, and which old headers to drop.

Security headers are instructions your server sends with each response, telling the browser to switch on protections it otherwise leaves off. They're cheap to add, they block whole categories of attack, and scanners (and security-conscious customers) check for them. Here's the set worth having, what each one does, and safe starting values.

The headers

Strict-Transport-Security (HSTS)

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

Tells the browser: "only ever connect to this site over HTTPS, for the next two years". Even if someone types http:// or follows an old link, the browser upgrades it — preventing downgrade attacks on public Wi-Fi.

  • Only send it once HTTPS works everywhere on the domain. (Mixed content errors.)
  • includeSubDomains applies it to every subdomain — make sure they all have HTTPS.
  • Adding preload and submitting your domain to browsers' preload list hard-codes it into browsers. It's very hard to undo; only do it when you're certain.
  • Start with a short max-age (say, 300) to test, then raise it.

Content-Security-Policy (CSP)

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-RANDOM'; img-src 'self' data: https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'

The most powerful header: a whitelist of where scripts, styles, images and other resources may load from. It's your strongest defence against XSS — even if an attacker injects a <script>, the browser refuses to run it. It's also the most work to get right, so it has its own guide: Content Security Policy: a practical guide. Roll it out with Content-Security-Policy-Report-Only first, which reports violations without blocking anything.

X-Content-Type-Options

X-Content-Type-Options: nosniff

Stops browsers "guessing" a file's type. Without it, an uploaded file served as text could be interpreted as a script. Always set it.

Frame protection: frame-ancestors / X-Frame-Options

Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY

Prevents other sites from embedding yours in an invisible iframe and tricking users into clicking buttons — clickjacking. frame-ancestors (part of CSP) is the modern way; X-Frame-Options covers older browsers. Use 'self' / SAMEORIGIN if you embed your own pages.

Referrer-Policy

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

Controls how much of your URL is sent to other sites when users click links or your page loads external resources. This value sends only your domain to other sites, not the full path — so tokens or private paths in URLs (like /reset-password?token=...) don't leak. It's the default in modern browsers, but setting it explicitly is good practice; use no-referrer on especially sensitive pages.

Permissions-Policy

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

Turns off browser features your site doesn't use, so a malicious script or embedded third party can't request them. List the features you don't need with empty ().

Cross-Origin-Opener-Policy

Cross-Origin-Opener-Policy: same-origin

Isolates your page from windows it opens or is opened by, protecting against some cross-window attacks. Can break OAuth pop-up logins or payment pop-ups — use same-origin-allow-popups if so.

Headers to stop sending

  • X-XSS-Protection — the old browser XSS filter is gone from modern browsers and could itself introduce bugs. Omit it (or send 0). CSP replaces it.
  • X-Powered-By: Express (and similar) — advertises your stack for free. Remove it (app.disable("x-powered-by") in Express; poweredByHeader: false in Next.js).
  • Server version numbers — remove version details where your server allows.

Copy-paste setup

Next.js

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

module.exports = {
  poweredByHeader: false,
  async headers() {
    return [{ source: "/(.*)", headers: securityHeaders }];
  },
};

(Add CSP separately — Next.js documents a nonce-based setup using middleware.)

Nginx

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

always makes Nginx send them on error responses too. Note that add_header inside a location block replaces — not adds to — headers set at a higher level.

Checking your headers

  • Browser DevTools → Network → click the page request → Response Headers. (Browser developer tools for beginners.)
  • curl -sI https://yourdomain.com
  • Free online scanners grade your headers and explain what's missing.

What headers don't do

Headers harden the browser side. They don't fix server-side bugs: broken access control, SQL injection, leaked secrets. They're one layer of several. (OWASP Top 10 explained.)

The summary

  • HSTS forces HTTPS; CSP blocks injected scripts; nosniff stops type-guessing; frame protection stops clickjacking.
  • Referrer-Policy and Permissions-Policy limit leakage and unused features.
  • Drop X-XSS-Protection and X-Powered-By.
  • Set them once in your framework or proxy config, check with DevTools or curl.

EasySpawn serves every app over HTTPS on your own domain from day one — the prerequisite for HSTS — and Claude Code can add the headers and verify them with curl against the live site. See how it works or join the waitlist.

Related: CSRF Explained · Reverse Proxies Explained · What Is HTTPS? · Vibe Coding Security Checklist

Keep reading