The security headers you can add in one deploy
A missing security header is the classic scanner freebie — the kind of finding a shakedown artist pads their email with because it costs them nothing to spot. Good news: it costs you almost nothing to fix. Most of them are a few lines of config you set once.
We've covered the first two legs of basic web hygiene: locking down email with DMARC, and not leaving your secrets on the internet. Security headers are the third. They're the instructions your server hands the browser about how to treat your site — and skipping them leaves easy, screenshot-ready findings lying around.
What a security header actually is
Every time your server answers a request, it sends back a set of response headers — little key-value lines the browser reads before it renders anything. Most are boring plumbing (Content-Type, Cache-Control). A handful are security directives: they tell the browser “only load me over HTTPS,” or “don't let other sites put me in an iframe.” Set them and the browser enforces the rules for you. Leave them off and the browser assumes the permissive defaults from a more trusting era of the web.
The headers worth setting
Strict-Transport-Security (HSTS)
Forces every future visit over HTTPS, even if someone types http:// or clicks an old link. Shuts down the downgrade attack where an attacker on the network strips your TLS and reads the plaintext. One header, enormous payoff.
Content-Security-Policy (CSP)
The heavy hitter, and the fiddly one. It whitelists where scripts, styles, and other resources are allowed to load from — which is the single most effective defense against cross-site scripting (XSS). It's also the one most likely to break your site if you slam it on blindly, so it gets its own rollout section below.
X-Frame-Options (or CSP frame-ancestors)
Stops clickjacking — someone loading your site invisibly in an iframe over their own page to trick your users into clicking things. Set it to DENY unless you specifically need to be embedded, in which case use SAMEORIGIN.
X-Content-Type-Options: nosniff
Tells the browser to trust your declared content types instead of guessing (“sniffing”) them. Stops a file you serve as data from being reinterpreted and executed as a script. Zero downside, always set it.
Referrer-Policy
Controls how much of your URL gets leaked to sites your users click through to. Without it, the full URL — query params and all — can be handed to third parties. strict-origin-when-cross-origin is the sensible default.
Permissions-Policy
Explicitly switches off browser features you don't use — camera, microphone, geolocation. If your app never needs the mic, saying so means a compromised script can't quietly ask for it either.
The copy-paste version
Here are the five safe ones — everything except CSP, which needs tuning. These rarely break anything, so you can ship them today:
Where you put them depends on your stack:
- Behind nginx? An
add_headerline per header inside yourserverblock — for exampleadd_header X-Frame-Options "DENY" always;. - On a framework or platform (Next.js, Remix, Vercel, Netlify, Cloudflare Pages)? Each has a spot to set response headers — a config file or a dashboard rule. Set the same key-value pairs there.
- Fronted by a CDN? You can often add them as an edge rule and cover every origin behind it at once.
One tip that saves confusion: apply them to every response, not just your HTML pages. On nginx that's the always flag — otherwise the headers vanish on error pages and redirects, and a scanner will still flag you.
Don't break your own site: rolling out CSP
CSP is the one that repays a little patience. A too-strict policy will silently block your own scripts, styles, and fonts — a blank page is a worse morning than a scanner finding. Walk it up:
- 1
Ship the safe five first.
Everything in the block above. That alone clears most of what a scanner flags, with basically no risk of breakage. - 2
Add CSP in report-only mode.
SendContent-Security-Policy-Report-Onlyinstead of the enforcing header. The browser reports what would have been blocked without actually blocking anything. Start from a strict base likedefault-src 'self'and watch the console. - 3
Widen it until your own site is clean.
Add the specific sources your app genuinely needs — your analytics domain, your font host, your image CDN — one at a time, until the report-only violations are just noise from browser extensions. - 4
Flip it to enforcing.
Drop the-Report-Onlysuffix. Now the policy is live, and an injected script from anywhere you didn't whitelist simply won't run.
That's the whole job. The safe five take one deploy; CSP takes one deploy plus a couple of days of watching reports. When the next lowlife runs their scanner, the headers section of their extortion email comes back empty — and you've quietly shut down a real class of attack while you were at it.
Want to see which headers you're missing right now? The scan checks every one of these in about 20 seconds.
Scan your site free →