Security headers: what each one prevents, in plain terms
Published · updated
Six response headers decide what a browser is allowed to do with your page. They are not a substitute for patching — but they are the difference between a small bug and a stolen session, and they take minutes to set.
Read your own headers first
curl -sI https://example.com/ | grep -iE 'strict-transport|content-security|x-frame|x-content-type|referrer-policy|permissions-policy'
Anything missing from that output is a decision you have not made yet — the browser will fall back to its permissive default. Our checker grades each of the six separately and explains the consequence rather than the acronym.
The six, by consequence
| Header | What breaks without it |
|---|---|
Strict-Transport-Security | The first request of a visit can travel over plain HTTP, and anyone on the network can read or rewrite it before the redirect happens. |
Content-Security-Policy | An injected script runs with your site's permissions — that is how a comment field becomes a session stealer. |
X-Frame-Options | Your pages can be embedded in someone else's frame, with your login form inside it, and clicks can be steered. |
X-Content-Type-Options: nosniff | A browser may treat an uploaded file as the type it guesses rather than the type you declared — an image that executes as a script. |
Referrer-Policy | Full URLs — including tokens in query strings — leak to third-party sites through the Referer header. |
Permissions-Policy | Embedded or injected code can ask for camera, microphone or geolocation without your pages having a say. |
Copy-ready configuration
nginx — in the server block, inside add_header with always so error pages get them too:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options "nosniff" always; add_header X-Frame-Options "SAMEORIGIN" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'" always;
Apache — with mod_headers enabled:
<IfModule mod_headers.c> Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()" </IfModule>
For a site behind Cloudflare or another proxy, set them at the origin as well: a rule in the CDN is invisible to anything talking to your server directly.
Two ways to get CSP wrong
Shipping the example policy. The script-src 'self' above breaks every inline script, analytics tag and third-party widget on a real site. Start with Content-Security-Policy-Report-Only, collect the violation reports for a week, then enforce — the header takes effect on the day you switch, and every broken page is your own outage.
Turning on HSTS preload too early. includeSubDomains; preload is essentially irreversible: browsers will refuse plain HTTP for every subdomain for the lifetime of the entry, including the one subdomain that still has no certificate. Ship max-age first, extend it once you know every subdomain is HTTPS-only.
Do not chase the letter grade
Header graders reward max-age length, penalise 'unsafe-inline' and hand out an A to sites with an unpatched PHP build, because headers are all they can see. A site can score A and be exploitable the same afternoon. Fix the interpreter first (patch checklist), then the headers that block the specific trick your stack is exposed to.
Questions
Do security headers protect against SQL injection or RCE?
No. They constrain what a browser may do with a page. A bug that executes on the server — like the two PHP CVEs of 21 October 2026 — is not affected by any header you send.
Is X-Frame-Options still needed if I set frame-ancestors in CSP?
CSP frame-ancestors supersedes it in modern browsers, but old clients only understand X-Frame-Options. Sending both costs one line and avoids deciding which browsers you care about.
Our site is behind Cloudflare — where should the headers live?
At the origin. Cloudflare can add or transform headers, but a request that reaches the server directly bypasses the edge. Set them in nginx or Apache, then verify with curl against the origin IP.
Does HSTS break my certificate setup?
It enforces HTTPS for the max-age period, so any subdomain served over HTTP will stop working — that is the point, and also the risk. Test with a short max-age first.