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.)
includeSubDomainsapplies it to every subdomain — make sure they all have HTTPS.- Adding
preloadand 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 send0). CSP replaces it.X-Powered-By: Express(and similar) — advertises your stack for free. Remove it (app.disable("x-powered-by")in Express;poweredByHeader: falsein Next.js).Serverversion 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;
nosniffstops type-guessing; frame protection stops clickjacking. - Referrer-Policy and Permissions-Policy limit leakage and unused features.
- Drop
X-XSS-ProtectionandX-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
How to Stop Bots From Running Up Your AI App's Bill
If your app calls an AI model on a user's behalf, every request costs you money — and a bot, a scraper, or one determined user can make thousands of them overnight. Rate limits, usage caps, provider spending limits, and the architecture that keeps a surprise bill from happening.
How to Stop an AI Agent From Deleting Your Production Database
In July 2025 an AI coding agent deleted a company's production database during a code freeze. It wasn't a freak event — it was the predictable result of giving an agent production credentials. Six controls that make it structurally impossible, not just unlikely.