What security headers actually stop, one at a time
The six response headers a scanner checks for, what each one actually prevents at the browser level, and the one honest caveat: none of them fix a vulnerability in your application code.
Every security scanner, this one included, checks for the same six response headers and reports whichever ones are missing. That list is useful and also easy to cargo-cult: paste six lines into your nginx config, watch the score go up, move on without knowing what any of them actually do. Here is what each one stops, specifically, at the browser level — and the one thing none of them do.
The one thing to get straight first
These headers are instructions to the browser, not defenses on your server. They reduce what a browser is willing to do once something has already gone wrong — a script got injected, a page got framed, a cookie got read by the wrong code. None of them stop the injection itself. A CSP is a very good second line of defense against cross-site scripting; it is not a substitute for escaping user input correctly the first time. Keep that distinction in mind for everything below.
HSTS (Strict-Transport-Security) — stops the first request from being plain HTTP
Without HSTS, a browser that hasn't visited your site before — or whose 30-day-old memory of your site has expired — tries plain HTTP first, then gets redirected to HTTPS by your server. That first request is the gap: on a hostile network, an attacker can intercept it and simply not forward the redirect, keeping the victim on HTTP for the whole session. This is SSL stripping, and it's an old, well-understood attack, not a theoretical one.
HSTS closes the gap by telling the browser, after the first successful HTTPS visit, to rewrite every future http:// request to https:// internally — before anything goes over the network. The preloaddirective closes it on the very first visit too, by getting your domain baked into the browser itself, but only once you've submitted it to the preload list.
Full writeup: HSTS explained.
CSP (Content-Security-Policy) — stops the browser from running a script it shouldn't
CSP names which origins a page is allowed to load scripts, styles and other resources from. Anything not on that list is blocked by the browser itself, independent of what your HTML or JavaScript tries to do. If a comment field, a compromised third-party widget, or a stored XSS bug manages to inject a <script>tag into your page, a correctly configured CSP stops the browser from executing it, because the injected content doesn't come from an allowed source.
This is the header where cargo-culting actually breaks things. default-src * passes a scanner check and protects nothing. A policy worth having takes iteration — start with Content-Security-Policy-Report-Only and a report-uri, watch what it would have blocked, and only switch to enforcing once the violation reports are clean.
Full writeup: Content-Security-Policy explained.
X-Frame-Options — stops your page being loaded inside someone else's
This one is about clickjacking: an attacker embeds your login page, payment form, or admin panel inside an invisible or disguised <iframe> on their own site, then tricks a visitor into clicking what looks like a normal button but is actually a button on your embedded page. X-Frame-Options: DENY blocks framing outright; SAMEORIGIN allows it only from pages on your own domain. The modern replacement is Content-Security-Policy: frame-ancestors, which supports an actual allow-list instead of an all-or-nothing rule — worth using if you have partners who legitimately need to embed you.
Full writeup: X-Frame-Options explained.
X-Content-Type-Options — stops the browser guessing what a file is
Without nosniff, some browsers inspect a response's bytes and decide for themselves what kind of content it is, even when your server's Content-Typeheader says otherwise. Historically this let an attacker upload a file that is technically an image but gets MIME-sniffed and executed as HTML or JavaScript — turning an upload feature into a stored-XSS vector. Modern browsers sniff far less aggressively than they used to, which is why this is usually rated lower severity than HSTS or CSP, but it's a one-line header with no downside, so there's no real reason to skip it.
Full writeup: X-Content-Type-Options explained.
Referrer-Policy — stops your URLs leaking to whoever a visitor clicks through to
URLs often carry more than a path: session tokens, search terms, internal document IDs, sometimes a password-reset token sitting in a query string. Without a Referrer-Policy, a browser may send the full referring URL to every link a visitor clicks, including third-party analytics and ad networks, leaking whatever was in that URL off your domain. strict-origin-when-cross-origin — the current browser default, worth setting explicitly rather than relying on the default staying put — sends the full URL only to same-origin requests, just the origin to cross-origin ones, and drops it entirely on an HTTPS-to-HTTP downgrade.
Full writeup: Referrer-Policy explained.
Permissions-Policy — stops embedded content asking for your camera on your behalf
Permissions-Policy switches off browser APIs your page doesn't use — camera, microphone, geolocation, USB, payment, and dozens more — for both your own code and anything embedded in the page. geolocation=()means nobody, including your own page, can request it. Most sites never need most of these APIs; explicitly disabling the ones you don't use means a compromised third-party script or a malicious ad in an iframe can't trigger a permission prompt for a capability the top-level page already switched off. It's defense-in-depth rather than a fix for a specific bug, which is why it's usually the lowest-severity finding on this list — and also why it's the one people skip.
Full writeup: Permissions-Policy explained.
What this doesn't cover
Six headers, one paragraph each, is not a security program. Headers say nothing about your certificate, your DNS, your SPF/DMARC setup, or whether your redirect chain actually forces HTTPS — all of which are separate categories a header-only checklist misses entirely. It also says nothing about the actual application logic: headers are a browser-side backstop, not a substitute for validating input or reviewing what your code does with it. A scan that checks header presence and configuration is a posture check, not a vulnerability scan or a penetration test — worth knowing what you're actually being told before you treat a clean scan as more than it is.