The Host Header Layer of app.onepassapps.com: One 403, One 301, One 404 and One 530 From the Same URL
I work on OnePassApps - this is our own site, and every number below was measured today, 11 November 2026, from outside the browser, signed out, over plain HTTPS, with no cookies and no JavaScript. Our last post measure
I work on OnePassApps - this is our own site, and every number below was measured today, 11 November 2026, from outside the browser, signed out, over plain HTTPS, with no cookies and no JavaScript.
Our last post measured what the Accept header does to one of our pages. This one goes a layer further down: the header that decides which site answers at all. Send the same request, to the same address, with a different Host value, and the answer changes - and our two hosts do not agree about what a wrong Host means.
What I measured
- 25 requests in the routing matrix - 11 to the app host, 9 to the API host, 4 that add a forwarding header, and 1 for the app host's own missing path
- 12 more in a second pass: byte-level diffs, three HTTP/2 repeats, and three more 403 fetches
37 requests in all, every one of them to our own two hosts, from one network.
One note on length before the numbers. The home page came back at 24,644 bytes in these runs, not the 25,011 our Accept post measured, because curl sends Accept: */* by default and our analytics beacon tag rides in only when the header names text/html. Same document, shorter one.
On the app host, every wrong name gets the same document
Host sent |
Status | Bytes |
|---|---|---|
app.onepassapps.com (control) |
200 | 24,644 |
APP.ONEPASSAPPS.COM |
200 | 24,644 |
App.OnePassApps.com |
200 | 24,644 |
app.onepassapps.com. (trailing dot) |
200 | 24,644 |
app.onepassapps.com:443 (port suffix) |
200 | 24,644 |
onepassapps.com |
403 | 553 |
api.onepassapps.com |
403 | 553 |
www.onepassapps.com |
403 | 553 |
nonexistent.invalid |
403 | 553 |
The four wrong names all get the same answer, and not merely the same length - the same bytes. One SHA-256 for all four: 982b986bb578e137f062099427a8caec3c501c84a9e4b22369ebd2badec42fe7. The same document also comes back for a wrong name on /guides, on /apps/paperkit, and on the API host, so it is one page in front of both hosts rather than two look-alikes.
That page is not ours. It says 403 Forbidden and cloudflare and nothing else, and it arrives with server: cloudflare and a cf-ray header, like every other answer in this post.
Case, the trailing dot and the port suffix all still get the page: 24,644 bytes each time.
On the API host, the same URL answers five different ways
Host sent |
Status | Bytes | What comes back |
|---|---|---|---|
api.onepassapps.com (control) |
200 | 410 | our JSON health answer |
onepassapps.com |
301 | 167 | a redirect to the app host |
www.onepassapps.com |
530 | 17 | error code: 1016 |
app.onepassapps.com |
404 | 13,806 | our own not-found page |
nonexistent.invalid |
403 | 553 | the same 403 document |
The redirect carries the path across: /api/health with Host: onepassapps.com answers Location: https://app.onepassapps.com/api/health, and /api/products answers Location: https://app.onepassapps.com/api/products. Same 167-byte response both times.
The 404 is ours, not the edge's: 13,806 bytes of HTML titled Page not found — OnePassApps, the same length and the same document the app host serves for a missing path, apart from the 84 bytes described below.
The 530 is the edge's: 17 bytes of text/plain reading error code: 1016, and nothing else.
The forwarding headers we do not honour
X-Forwarded-Host is the header a proxy would use to rewrite the site a response belongs to, so it is worth knowing it changes nothing here. With Host: app.onepassapps.com and X-Forwarded-Host: evil.example added, the answer is still 200 with 24,644 bytes, the canonical link is still https://app.onepassapps.com/, the og:url is still the same, and evil.example appears nowhere in the document.
X-Forwarded-Proto: http behaves the same way, and so does the standard Forwarded header (for=1.2.3.4;host=evil.example;proto=http): 200, 24,644 bytes, canonical unchanged, and the injected host nowhere in the body.
So on the wire, the only header that picks the site is Host itself.
One document, and 84 bytes that move
Two fetches of the same URL do not match byte for byte: they differ in 84 bytes, all of them inside the /cdn-cgi/l/email-protection link, which is the per-request key our own earlier post measured. The two 404 pages differ by the same 84 bytes in the same place, which is why the API host's 404 for Host: app.onepassapps.com and the app host's 404 for a missing path are the same document to the byte, minus that key.
That is the same trap our Accept post named: compare lengths, not bytes.
What this is not
- These are edge answers.
curl -H "Host: ..."changes the header only - the TLS server name and the address the request reached stay the same - so this measures the routing layer and nothing below it. - The 403 and the 530 are Cloudflare's documents, not ours:
server: cloudflareon both. - No third-party host was contacted. The one name that does not belong to us,
nonexistent.invalid, is reserved by RFC 2606 and never resolves. - Nothing here was an attempt to make an answer change, to reach an origin directly, or to get past the 403. The 403 was measured and left alone.
- I did not follow the 301 to see where it lands, so this post does not claim what the app host does with
/api/health.
The tools are at app.onepassapps.com, and the guides for each one are on the guides page.
Where this sits against our other posts
Three earlier posts of ours touch this neighbourhood, and none of them sends a wrong Host:
-
What the Accept Header Decides on Our Own Site: Cloudflare's Beacon Tag Rides In or Out, and the API Host That Ignores It measures the
Acceptheader on the same two hosts. Different header, same hosts. -
The Origin Allow-List of api.onepassapps.com: Two Allowed Origins, One Fixed Preflight and Per-Route Rate Limits measures the
Originheader and which origins the API host accepts. That is a header the browser sets for us;Hostis the one the client sets to choose the site. - The Near-Miss Paths: 70 Requests, Ten Spellings, and the Dot-Segment Our Router Will Not Resolve measures ten spellings of a path on the app host. Those are misspellings after the site has been chosen; this post is about the choice itself.
The layer under this one, the handshake, is in The TLS Layer Under app.onepassapps.com.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.