LW IT Solutions
« Blog Overview /IT & Networks/Tutorials / HTTP Security Headers Explained: Which Ones Protect...

HTTP Security Headers Explained: Which Ones Protect and Which Are Merely Present

HTTP Security Headers Explained: Which Ones Protect and Which Are Merely Present
Contents
  1. What the security headers are
  2. The rule that unsafe-inline cancels
  3. How long an HSTS lifetime has to be
  4. When two headers govern the same thing
  5. What travels along with every click
  6. What a single fetch shows
  7. Questions and answers
  8. Sources

Every page a server sends comes with a set of headers – lines that travel ahead of the content and tell the browser how to treat what follows. A handful of them are about safety.

They are easy to check and easy to check wrongly. Counting which headers are present takes a second and produces a number that looks like a result. The number says almost nothing, because a header can be present, correctly spelled, and completely without effect.

Four security headers from one response, each with its value and what it actually achieves: two work, two cancel themselves out
All four are present. Two of them protect nothing.

What the security headers are

Four of them carry most of the weight, and each answers a different question.

Content-Security-Policy lists where the page is allowed to load code from. Anything not on the list is refused by the browser, which is the main defence against injected scripts.

Strict-Transport-Security tells the browser to use the encrypted connection from now on and never the plain one – including when a link, a bookmark or a typed address says otherwise.

X-Frame-Options decides whether the page may be embedded in a frame on someone else’s site, which is what stops a visitor from clicking on something invisible.

Referrer-Policy decides how much of the current address is passed on when a visitor follows a link away from the page.

A fifth, Permissions-Policy, switches off browser features the page does not need – camera, microphone, location. It is the least common of the five and the least likely to break anything.

The rule that unsafe-inline cancels

Content-Security-Policy is the strongest of the five and the one most often defeated by its own value.

The threat it exists for is an injected script: something a visitor typed, or an attacker planted, ends up in the page and runs as if it belonged there. CSP prevents this by naming the sources the browser may execute code from, and a script written directly into the page has no source to name.

Which is precisely what 'unsafe-inline' permits again. The word is in the value for a reason – it says what it does – and it is there because a page full of small inline scripts stops working without it. Adding it is the fastest way to make a broken page work again, and it gives back the exact permission the policy was written to withhold.

A policy containing 'unsafe-inline' still blocks a few things: code loaded from foreign addresses, for instance. Against the attack CSP is mainly for, it does close to nothing.

'unsafe-eval' is the same story for a narrower case, and a default-src of * is the same story without any pretence.

How long an HSTS lifetime has to be

Strict-Transport-Security has one number that decides everything: max-age, in seconds.

It says how long the browser should remember the instruction. Until it expires, a plain unencrypted request to that domain is never sent – the browser rewrites it before it leaves the machine. That is the whole protection, and it only holds for as long as the memory lasts.

A max-age of 300 remembers for five minutes. Formally correct, present in every check that counts names, and useless for a visitor returning the next day. The usual recommendation is a year, written as 31536000.

Two additions matter. includeSubDomains extends the rule to every subdomain, which closes the gap where a forgotten test system on the same domain gets reached unencrypted. And preload asks for the domain to be built into browsers directly, so the protection covers the very first visit too – the one moment where HSTS otherwise cannot help, because nothing has been remembered yet.

That last step deserves a warning: entries are hard to remove and removal takes months to reach browsers. It suits a domain whose encryption is settled, not one still being sorted out.

Four upright panels of equal size: two solid, two only outlines

When two headers govern the same thing

Framing is regulated twice, by two headers of different ages, and the rule for which one wins surprises people.

X-Frame-Options is the older one, understood everywhere, and offers DENY or SAMEORIGIN. CSP has frame-ancestors, which does the same job with a list of addresses instead of two fixed choices.

When both are present, frame-ancestors wins and X-Frame-Options is ignored entirely. Not merged, not combined – ignored. A page with X-Frame-Options: DENY and a CSP containing frame-ancestors * can be framed by anyone, and the stricter of the two headers has no say.

This is the failure that hides best, because both headers are present, both are spelled correctly, and a check that counts names reports two protections where there is one.

What travels along with every click

Referrer-Policy is the quietest of the five and the one with the most everyday consequences.

When a visitor follows a link away from a page, the browser tells the destination where the visitor came from. How much it tells is what this header sets. unsafe-url passes the full address including the path; no-referrer passes nothing; strict-origin-when-cross-origin passes the full address within the same site and only the domain to foreign ones.

The path is where the sensitivity sits. /en/account/orders/48120 reveals a customer number, a password reset link reveals a token, and a search page reveals what was searched for. All of that goes to whichever site is linked to, in an ordinary header, without anything going wrong.

The last of the three values is the sensible default, and modern browsers apply it on their own when the header is missing. Which makes an explicitly set unsafe-url worse than no header at all: it replaces a good default with a bad decision.

What a single fetch shows

All of this is public. The headers arrive with every response, for any address, and reading them takes one request.

Four questions are worth asking. Does a CSP exist, and does its value give back what it took away. Is the HSTS lifetime long enough to survive between two visits. Do the two framing headers agree, and does the one that wins say what was intended. And does the referrer setting pass on more than the destination needs.

The HTTP security header checker answers all four for any address. It fetches the page once, reads the headers as a browser reads them, follows the redirects on the way, and judges the values rather than counting the names.

The useful result is not a grade. It is the short list of headers that are present and doing nothing – because those are the ones that will not be looked at again.

Questions and answers

How can ‘unsafe-inline’ be dropped without moving every inline script out of the page?

With nonces or hashes. A nonce is a random value that appears in the CSP as ‘nonce-…’ and as an attribute on every permitted script; the browser then only runs inline scripts that carry this value. A hash (‘sha256-…’) permits a script with exactly that content. An injected script neither knows the value nor matches the hash, and that is precisely what restores the protection ‘unsafe-inline’ took away.

As soon as a nonce or a hash appears in the policy, current browsers ignore an ‘unsafe-inline’ next to it. It can therefore stay as a fallback for very old browsers without weakening the protection in the others.

Two constraints follow. A nonce has to be generated afresh for every response; a page cache that serves the same HTML with the same nonce to everyone makes it known and therefore worthless, which is why hashes work better with caches. And event attributes such as onclick are not covered by nonces at all, and by hashes only with the additional keyword ‘unsafe-hashes’; moving them into scripts is the cleaner route. Until everything is in place, the Content-Security-Policy-Report-Only header shows what a policy would block without blocking it.

Does an HSTS header sent over an unencrypted connection have any effect?

No. Browsers only honour Strict-Transport-Security in responses over HTTPS and ignore the header in an unencrypted response, because anyone along the way could have inserted or removed it there. The response on http:// should therefore only redirect to HTTPS, and the header belongs in the response that redirect leads to.

What happens when both the web server and the application send a CSP?

Both apply. The browser does not merge them into one common policy but checks each on its own, and a resource is only loaded if every policy permits it. A second, looser CSP therefore cannot weaken a strict one; a forgotten strict policy, in the web server configuration for instance, on the other hand blocks things the application expressly permits in its own policy.

Strict-Transport-Security behaves differently: if the header arrives twice, the browser processes only the first one, as RFC 6797 requires. When headers are set in several places, such as the web server, a plugin and a CDN, the response actually delivered is what needs checking, not the individual configurations.

Can frame-ancestors also be set in a meta element?

No. A CSP in a meta element does not support frame-ancestors, nor report-uri or sandbox, and X-Frame-Options likewise only works as a real header. Protection against embedding therefore always needs a setting on the server or in the CDN.

Lukas Wojcik

Lukas Wojcik

Systems architect and technology enthusiast specializing in scalable tracking solutions, GMP Stack (GA4 & GTM), and robust backend architectures. Advocate for clean code and privacy-first design.

Get in Touch

Briefly describe your project or inquiry for a tailored response. This site is protected by reCAPTCHA.

Write a comment

Experience with other hardware or firmware and questions about the configuration are welcome here.

The email address is not published. Required fields are marked with an asterisk.

Articles & categories

CCTV

Follow this category by RSS

Cloud & AI

All 17 articles in this category Follow this category by RSS

Data Privacy

All 19 articles in this category Follow this category by RSS

Digital Analytics

All 58 articles in this category Follow this category by RSS

Digital Marketing

All 37 articles in this category Follow this category by RSS

IT & Networks

All 19 articles in this category Follow this category by RSS

Music Production

All 17 articles in this category Follow this category by RSS

Raspberry PI

Follow this category by RSS

SaaS & Internet Earning

Follow this category by RSS

Smart Home

All 18 articles in this category Follow this category by RSS

Web Development

All 11 articles in this category Follow this category by RSS

WordPress Plugins & Tricks

All 14 articles in this category Follow this category by RSS