Questions

Why do my security headers disappear in production?

Because a layer between your application and the visitor is stripping or overwriting them — usually a CDN, a reverse proxy, or a platform that applies its own header set. The application is almost never wrong in this scenario: it is sending the headers, and something downstream is removing them. Comparing the origin's response against the public hostname identifies the layer in a single pair of requests.

Compare the two responses first

`curl -sI https://www.example.com` gives what visitors receive. Requesting the origin hostname directly, where you have one, gives what the application sends.

Headers present at the origin and absent publicly means the edge is removing them. Absent in both means the application never sent them.

Where no separate origin hostname exists, a local run of the same application is the next best comparison.

Record both outputs rather than reading them. The difference is usually two or three header names, and it is easy to misremember which side had what.

Test an asset and an error page as well as the document. Header rules applied to HTML only are common, and a 404 served without them is still a page an attacker can frame.

The layers that rewrite headers

A CDN applying a managed header set, which frequently replaces rather than merges — so setting a header at the origin has no effect at all.

A reverse proxy with an explicit header policy, added once for a good reason and never revisited.

A hosting platform that injects its own defaults, which can be stricter or laxer than yours.

A framework or middleware that sets headers on some responses and not others, so the home page is protected and an API route is not.

A configuration file applying to one path prefix only, which is the version that produces headers present on half the site.

Duplicates are worse than absences

When two layers each set the same header, the browser may see it twice, and behaviour differs by header.

Two Content Security Policies are both enforced, and the effective policy is their intersection — stricter than either author intended, which is how a policy blocks things neither configuration mentions.

Two `Strict-Transport-Security` headers are ambiguous, and the browser's choice is not something to rely on.

`curl -sI` lists every occurrence, so a header appearing twice in the output is the finding.

`X-Frame-Options` duplicated with conflicting values is treated as invalid by some browsers, which means the page ends up with no framing protection at all.

Where to set them so they survive

Set them at the outermost layer that serves the visitor, which is usually the CDN or the edge configuration rather than the application.

Set them in exactly one place. Two layers setting the same header is how the duplicates above happen.

Apply them to every response, not only to HTML documents. Assets, API routes and error pages are responses too, and error pages are the ones most often missed.

Check them again after any platform change. A CDN migration resets header behaviour silently, and nothing on the site looks different.

Verifying across the whole site

VeriFixScan reports `security.csp`, `security.x_content_type_options`, `security.x_frame_options`, `security.referrer_policy` and `security.permissions_policy` from the responses it actually received.

Because it reads the public hostname, it reports what visitors get rather than what the configuration intends — which is the useful direction for this problem.

Checking more than one page is what finds the per-route case, where a header exists on the home page and nowhere else.

`security.server_disclosure` reports a `Server` or version header, which frequently names the layer that is doing the rewriting.

Frequently asked questions

My headers are in the config but not in the response. Why?
A CDN or proxy in front is replacing the header set rather than merging it. Compare the origin's response with the public one to confirm.
What happens if two layers both set a CSP?
Both are enforced and the effective policy is their intersection, which is stricter than either author intended and blocks things neither configuration mentions.
Where should security headers be set?
At the outermost layer serving visitors, in exactly one place, applied to every response including assets and error pages.

Sources

Related

VeriFixScan crawls a site and applies its checks to every page it reaches, keeping the evidence behind each finding. Scanning one website is free.

Scan a website