Security headers — CSP, HSTS, and what each one actually blocks
A handful of HTTP response headers let the SERVER tell the BROWSER to enforce a real security restriction on its own behalf — each one closes a specific, different attack path, and none of them is a general-purpose "turn on security" switch.
4 min read
Content-Security-Policy (CSP): a real, enforced allowlist for what a page can load and run
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com
Even if an XSS vulnerability lets an attacker inject <script>evil()</script>
into the page, a CSP restricting script-src to 'self' and one trusted CDN
means the BROWSER ITSELF refuses to execute that injected script, since
its source doesn't match anything the policy allows
CSP tells the browser exactly which sources are allowed to load scripts, styles, images, and other resources for a page — and critically, the browser enforces this itself, refusing to execute or load anything outside the declared allowlist, regardless of how it got into the page's HTML. This is a genuine, real defense-in-depth layer specifically against XSS (the previous lesson): even if an attacker successfully injects a <script> tag through some XSS vulnerability, a correctly configured CSP can prevent that injected script from actually running, since inline scripts (or scripts from untrusted origins) are exactly what a well-configured script-src directive blocks.
Strict-Transport-Security (HSTS): forcing HTTPS, even on the very next visit
Strict-Transport-Security: max-age=63072000; includeSubDomains
Without HSTS: a user typing "example.com" (no https://) gets an initial
plain HTTP request, which an attacker on the same network (a coffee-shop
WiFi) can intercept and manipulate BEFORE any redirect to HTTPS happens
With HSTS: once a browser has seen this header from example.com, it
REFUSES to ever make a plain HTTP request to that domain again, for the
duration set by max-age — automatically upgrading every future request
to HTTPS itself, before any network request is even attempted
HSTS closes a real, specific gap: a user typing a bare domain (no protocol) initially connects over plain HTTP, and an attacker positioned on the network path (an open WiFi network, a compromised router) can intercept that first plain-HTTP request before any server-side redirect to HTTPS ever happens — a real attack called SSL stripping. Once a browser has received the HSTS header once, it remembers to force HTTPS for that domain on every subsequent visit, entirely client-side, closing the plain-HTTP window that SSL stripping depends on.
X-Frame-Options / frame-ancestors: preventing your page from being embedded in someone else's
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Without this header, an attacker can embed your ENTIRE real page inside
an invisible <iframe> on their own malicious site, overlay convincing-
looking fake UI on top, and trick a victim into clicking what LOOKS like
the attacker's UI but is actually clicking buttons on YOUR real page —
this is "clickjacking," covered in its own lesson later in this domain
X-Frame-Options: DENY (or the modern CSP equivalent, frame-ancestors 'none') tells the browser to refuse to render this page inside a <iframe> on any other site at all — directly preventing the clickjacking attack shape, where a malicious page embeds a legitimate site invisibly and tricks a user into clicking real buttons on the real site while believing they're interacting with something else entirely.
X-Content-Type-Options: nosniff — stopping the browser from guessing a file's type
X-Content-Type-Options: nosniff
Without this header, a browser might "sniff" a file's actual content and
decide to treat a file the SERVER labeled as an image as if it were
HTML/JavaScript instead, if the file's bytes happen to look enough like
HTML — potentially executing script from what was supposed to be a
harmless, uploaded image file
By default, some browsers try to guess a resource's real type by inspecting its content, rather than trusting the server's own Content-Type header — a real, historical source of vulnerability when a user-uploaded file (nominally an image) gets sniffed and reinterpreted as executable HTML/JavaScript. nosniff tells the browser to trust the server's declared Content-Type exactly, closing this specific type-confusion attack path.
Why headers alone don't replace the underlying fixes covered elsewhere in this domain
These headers are real, genuine defense-in-depth — CSP specifically catches XSS that did happen, HSTS closes a specific network-level gap, frame options prevent clickjacking directly — but none of them is a substitute for the root fixes covered in this domain's other lessons (parameterized queries for SQL injection, output escaping for XSS, CSRF tokens for CSRF). A misconfigured or missing CSP doesn't cause an XSS vulnerability to exist in the first place; it removes one layer that could have limited the damage if a real XSS bug is present — the actual bug still needs its own actual fix.
Further reading
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. How does Content-Security-Policy provide real defense-in-depth against XSS?
2. What real attack does HSTS specifically close?
3. What attack does `X-Frame-Options: DENY` (or CSP's frame-ancestors) prevent?