Code Safari

Chapter 99·Intermediate·10 min read

Security Headers Explained: Hardening a Site With Almost No Code

A plain-English guide to HTTP security headers — Content-Security-Policy, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy and friends. What each one defends against, why CSP is the big one, and how to add real protection with response headers alone.

July 28, 2026

The last two chapters kept mentioning protective HTTP headers in passing — HSTS, CSP. Time to meet the whole family. They share a delightful property: each is a short instruction the server sends with its responses, and the browser enforces it. No refactor, no new dependency — a few lines of config that shut down entire attack classes.

This is OWASP's "security misconfiguration" territory, where the vulnerability is usually absence — the header that was never set.

The shared mechanism

Every header here works the same way: it rides along in the HTTP response, and a modern browser reads it as a rule to enforce on that page.

Server adds response headers
Browser reads them per page
Browser enforces the rules
Whole attack classes closed
One mechanism, many protections: the server instructs, the browser enforces.

Because enforcement is the browser's job, these are true defence-in-depth: they don't remove the underlying bug, they give the browser standing orders that contain it. Now the family, most-impactful first.

Content-Security-Policy: the big one

CSP is the most powerful and the most involved. It answers a single question for the browser: from where is this page allowed to load and run resources? You declare the allowed sources; the browser refuses everything else.

Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com

That says: load resources only from my own origin, and run scripts only from my origin and cdn.example.com. The security payoff connects straight back to XSS: suppose an attacker does inject <script>steal()</script> into your page. With this policy, the browser sees an inline script the policy never allowed — and refuses to execute it. The injection reached the page; the CSP defused it.

CSP does more than scripts — frame-ancestors handles clickjacking (below), img-src, connect-src, style-src lock down other resource types — but "control what scripts run" is its headline job.

X-Frame-Options / frame-ancestors: stop clickjacking

Clickjacking is an attack we haven't met yet, and it's sneaky. The attacker loads your real site inside an invisible <iframe> on their page, overlays their own decoy content, and lines things up so that when the victim clicks the visible "Win a prize!" button, they're actually clicking the invisible "Delete my account" or "Authorise payment" button on your framed site — with their real session. The user attacks themselves without knowing your site is even involved.

The defence is to forbid other sites from framing you:

Content-Security-Policy: frame-ancestors 'none'
X-Frame-Options: DENY

frame-ancestors (the modern CSP directive) and the older X-Frame-Options both tell the browser who, if anyone, may embed your page in a frame. Set them to none/DENY unless you have a specific reason to be embeddable, in which case allowlist the exact parent origins. No frame, no clickjack.

X-Content-Type-Options: nosniff

Browsers historically tried to be helpful by guessing a response's real type when the declared Content-Type looked wrong — "MIME sniffing." Helpful, and dangerous: an attacker uploads a file that claims to be an image but contains HTML/JavaScript; a sniffing browser "corrects" it to a script and runs it, straight from your domain. One header ends the guessing:

X-Content-Type-Options: nosniff

It tells the browser: trust the declared Content-Type, never second-guess it. Especially important on any endpoint serving user-uploaded files (which should also be served from a separate origin — see defence in depth).

The rest of the useful set

HeaderWhat it doesWhy it matters
Strict-Transport-SecurityForces HTTPS-only for the domainCloses the SSL-strip downgrade window
Referrer-PolicyControls how much URL is sent in the Referer header when leaving your siteStops leaking session tokens or private paths embedded in URLs to third parties
Permissions-PolicyAllow/deny browser features (camera, mic, geolocation) per pageLimits what injected or embedded content can ask the browser to do
Set-Cookie: HttpOnly; Secure; SameSiteCookie flags (technically per-cookie)HttpOnly blocks JS cookie theft via XSS; SameSite blocks CSRF; Secure keeps cookies off HTTP

That cookie row ties three chapters together: the humble Set-Cookie flags are where the XSS, CSRF, and HTTPS defences all converge on a single line of config.

What to actually do

The pragmatic playbook, since the failure mode is absence:

  1. Scan your site — free tools (like Mozilla Observatory or securityheaders.com) grade your headers and show what's missing in minutes.
  2. Add the cheap wins immediatelyHSTS, nosniff, X-Frame-Options: DENY, sensible Referrer-Policy, and the cookie flags are near-zero-risk to enable.
  3. Treat CSP as a project — start in report-only, iterate on the violations, then enforce. It's the highest-value header and the one that rewards patience.

Recap

  • Security headers are browser instructions sent with each response — a handful of config lines that close whole attack classes as defence-in-depth.
  • CSP is the big one: it controls which scripts/resources may run and can neutralise an injected script even after XSS slips through — a backstop to encoding, best rolled out in report-only first.
  • frame-ancestors/X-Frame-Options stop clickjacking (your site framed invisibly over a decoy).
  • nosniff stops MIME-sniffing that can execute an uploaded "image" as a script.
  • HSTS, Referrer-Policy, Permissions-Policy, and cookie flags round out the cheap, high-value set — the cookie flags alone anchor the XSS, CSRF, and HTTPS defences.
  • Headers harden, they don't fix — they're the outer ring, not a replacement for the inner ones.

We've collected a lot of individual defences. The final chapter steps back to the philosophy that ties them together — and the operational habits that keep a site secure over time. Continue to Defence in depth.

Security Headers Explained: Hardening a Site With Almost No Code | Code Safari