HTTP headers are the polite agreement between a server and a browser. The server sends instructions, the browser follows them. The security headers take about ten minutes to set, cost nothing, and eliminate entire categories of attack. If you're on Cloudflare or any modern CDN, most of them are a dropdown.
Here's what to set, in order of how often I see them missing.
Strict-Transport-Security
HSTS is the one I find missing most. It tells the browser "once you've connected over HTTPS, never fall back to HTTP for this domain". Without it, the user's first visit is still vulnerable to downgrade on untrusted networks (airport wifi, random cafe, a mall with one of those fake captive portals that some script kiddie set up for a laugh).
Start at max-age=15552000; includeSubDomains. Six months. If nothing catches fire, move to max-age=63072000; includeSubDomains; preloadand submit to the HSTS preload list. The preload entry bakes your domain into every major browser's default behaviour, so even first-time visits are HTTPS-only.
The catch: includeSubDomains means every subdomain now has to serve HTTPS. If you forgot about old-blog.yourdomain.comand it's been HTTP-only since 2019, it'll become unreachable for anyone who's already hit the main site. This is also a feature, not a bug. You should find that old blog and deal with it.
Content-Security-Policy
CSP is the strongest header and the one most people half-set. It says which sources the browser is allowed to load scripts, styles, and images from. A restrictive policy turns most XSS bugs from "the attacker ran code" into "the browser refused".
The typical half-set version is something like default-src 'self' 'unsafe-inline' 'unsafe-eval' *, which is the same as not having CSP at all. The version that actually does something is restrictive and specific, with every third-party origin (analytics, chat widget, fonts) listed explicitly. Start in report-only mode, set a reporting endpoint, watch the violations roll in for a week, then harden. I use CSP in report-only for a month on new client sites before flipping it on.
If you have inline scripts you can't remove, use a nonce. Not 'unsafe-inline'.
Frame-ancestors
This is the modern way to say "don't let other sites embed mine in an iframe". Add frame-ancestors 'none' to your CSP. Done. The old X-Frame-Options: DENYheader still works and doesn't hurt, so set them both if you like.
Clickjacking is the attack this stops. Someone loads your login page inside their iframe, overlays invisible buttons on top, and tricks a logged-in user into clicking things they can't see. It's not theoretical. It was how a few Bitcoin exchanges lost money in the 2010s.
Referrer-Policy
Default value: strict-origin-when-cross-origin. This stops your internal URLs leaking to third parties when users click outbound links. Password reset pages routinely have the token in the URL. Without a sensible referrer policy, that token ends up in every ad network's log.
Permissions-Policy
This one opts you out of browser features you don't use. Camera, microphone, geolocation, autoplay. If a script gets loaded onto your page that shouldn't be, the permissions policy limits what it can do. A sensible starting value: camera=(), microphone=(), geolocation=(), interest-cohort=().
Two I see people still setting in 2026 and shouldn't
X-XSS-Protection was deprecated in 2020. Modern browsers ignore it. CSP does the job. Remove it from your config.
Expect-CT was deprecated in 2024. Same deal. Remove it.
Check what you have now
Easiest way: open your site, Chrome DevTools, Network tab, click the document request, look at Response Headers. Or run the free check, which grades your headers and points at the specific fix. If HSTS is missing, I'd start there.
Run your own external scan.
AttackEdge continuously scans what your business exposes to the internet, then translates the findings into plain English. See what attackers see, without hiring a security team.