The Header-Name Shapes of Our Two Hosts: Four on the App Host, Two on the API Host, Nine Names in Common
I work on OnePassApps, the team behind app.onepassapps.com, so this is our own build, and every number below was measured on it today over plain HTTPS from outside the browser - no session, no cookies, no JavaScript, one
I work on OnePassApps, the team behind app.onepassapps.com, so this is our own build, and every number below was measured on it today over plain HTTPS from outside the browser - no session, no cookies, no JavaScript, one fetch per URL, 8 November 2026.
Start at the app or read the guides.
An earlier post in this series counted the headers on 18 URLs and found one identical set. That is still true of the HTML pages, and it is still true of the 62 HTML responses measured here. The interesting part is what happens when you stop looking only at HTML: the site does not send one header set, it sends a small number of them, and which one you get tells you what kind of thing you asked for.
What was measured
88 requests in total: 75 on the app host and 13 on the API host.
- App host: all 56 URLs in the sitemap, 13 static files (the icons, the web app manifest, the crawler text files, the fallback stylesheet and script) and 6 routes added by hand - /apps, /support, /sign-in, /sign-up, /account, and one URL that does not exist.
- API host: 12 routes plus one that does not exist.
I compared header names, lower-cased, after dropping 10 names that describe the individual request rather than the policy: date, server, cf-ray, report-to, nel, alt-svc, connection, cf-cache-status, content-length and transfer-encoding. The first eight change from request to request. The last two are transport framing - a chunked body and a body with a declared length carry the same policy - so folding them into the shape would split every group in two for no policy difference.
Four shapes on the app host
| Responses | What they are | Extra names beyond the HTML set | Cache-Control |
|---|---|---|---|
| 62 | every HTML response, including the 404 | (none - this is the base set) | 61 x public, max-age=0, must-revalidate, 1 x no-store
|
| 7 | favicon.ico, apple-touch-icon.png, icon-192.png, icon-512.png, icon-maskable-512.png, og-image.png, robots.txt | etag, accept-ranges | public, max-age=14400, must-revalidate |
| 3 | favicon.svg, shell-fallback.css, shell-fallback.js | etag, vary | public, max-age=14400, must-revalidate |
| 3 | site.webmanifest, llms.txt, sitemap.xml | etag | public, max-age=0, must-revalidate |
The HTML set itself is 12 names: access-control-allow-origin, cache-control, content-security-policy, content-type, cross-origin-opener-policy, cross-origin-resource-policy, permissions-policy, referrer-policy, strict-transport-security, x-content-type-options, x-frame-options, x-permitted-cross-domain-policies.
Two things fall out of the table. An ETag appears on 13 of the 75 app-host responses and on 0 of the 62 HTML responses - it is on the static files and on nothing else. And the 7-response group is the only one that carries accept-ranges, so those files can be fetched in parts.
The vary header is on exactly 3 of the 75 app-host responses - the SVG, the stylesheet and the script - and its value there is accept-encoding.
Two shapes on the API host
12 routes carry an identical set of 22 names. The 13th, /api/guest-data/signaldesk, carries those same 22 plus one more: set-cookie. It is the only response of all 88 that sets a cookie at all, and the cookie is named toolpass_signaldesk_guest.
The API host is uniform where the app host is not: cache-control is private, no-store, max-age=0 on all 13 routes, and x-robots-tag: noindex is on all 13 of them. The app host sends x-robots-tag on none of its 75.
Nine names in common
Both hosts put these 9 names on every response they send: cache-control, content-type, cross-origin-opener-policy, cross-origin-resource-policy, referrer-policy, strict-transport-security, x-content-type-options, x-frame-options, x-permitted-cross-domain-policies.
Everything else splits. The app host adds 5 that the API host never sends: accept-ranges, access-control-allow-origin, content-security-policy, etag, permissions-policy.
The API host adds 13 that the app host never sends: access-control-expose-headers, cdn-cache-control, origin-agent-cluster, set-cookie, x-concurrency-limit, x-concurrency-remaining, x-dns-prefetch-control, x-download-options, x-ratelimit-limit, x-ratelimit-remaining, x-request-id, x-robots-tag, x-xss-protection.
The one header that varies by route
vary takes three different values across the 13 API routes: Origin on 7 of them, Accept-Encoding, Origin on 5, and Authorization, Cookie, Accept-Encoding, Origin on the single route that reads an authorization header, /api/voting. That last one is the only route in the whole sweep whose response depends on credentials - which is why it is the only one that advertises Authorization and Cookie in vary.
What I did not do
- No browser, no JavaScript, no session, no cookies. One fetch per URL.
- 8 November 2026, one build, one network.
- The 12 real API routes are a sample I chose by hand, not an inventory of the API - I am not claiming these two shapes cover every route the API has.
- I compared names, not values, except where a value is named explicitly above.
- I did not test whether any browser, cache or crawler acts differently because of any of this.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.