guide 2 of 3
Accept-CH & Critical-CH
Everything beyond the three default hints is negotiated. Two response headers run that negotiation, and the corner cases (first visits, caches, third parties) are where real deployments succeed or quietly fail.
Accept-CH: the grant
To receive optional hints, name them in an Accept-CH header on any response to a top-level navigation:
HTTP/1.1 200 OK
Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Platform-Version, Sec-CH-Prefers-Color-Scheme Four properties of the grant do most of the explaining:
- Per origin. The browser stores "this origin may get these hints" keyed to the exact scheme+host+port. A grant for
example.comsays nothing about its subdomains. - Persistent until replaced. The stored set lasts across visits, until the user clears site data or a later response sends a different
Accept-CH. Each response's list replaces the stored set entirely: sendingAccept-CH: Sec-CH-UA-Archtoday and an emptyAccept-CH:tomorrow revokes everything. There is no additive merge. - HTTPS only. Grants are ignored on plain HTTP.
- Top-level navigations set it. An
Accept-CHon a subresource (an image, an XHR) does not update the stored set, so put it on your HTML responses.
This site, for instance, sends its Accept-CH on every response via a static _headers file. That is why the reference tables can show your high-entropy values without any JavaScript negotiation.
The first-request gap
The grant mechanism has an inherent hole: the browser's very first request to your origin happens before it has ever seen your Accept-CH. That request carries only the default hints. Three ways to live with it:
- Accept it. Fine for analytics and any hint that improves rather than determines the response. Second page view onward is fully hinted.
- Recover in JavaScript.
navigator.userAgentData.getHighEntropyValues()returns the same data on the first page, client-side. - Force a retry with Critical-CH. Below.
Critical-CH: the retry
Critical-CH marks a subset of your Accept-CH list as render-critical:
HTTP/1.1 200 OK
Accept-CH: Sec-CH-Prefers-Color-Scheme, Sec-CH-UA-Arch
Critical-CH: Sec-CH-Prefers-Color-Scheme
Vary: Sec-CH-Prefers-Color-Scheme When a browser receives this and realizes it would have sent Sec-CH-Prefers-Color-Scheme had it known, it discards the response and retries the request with the hint attached, once, before rendering anything. Cost: one round trip, on the first visit only. Benefit: even the first response can be server-rendered in the right theme. The classic user is a dark-mode-aware server renderer (see Sec-CH-Prefers-Color-Scheme).
Every Critical-CH entry must also appear in Accept-CH; keep the critical list to genuinely render-blocking hints, because you are charging every new visitor a round trip for it.
Caching: Vary or suffer
The moment a response body depends on a hint, shared caches must key on it:
Vary: Sec-CH-UA-Mobile, Sec-CH-Prefers-Color-Scheme Rules of thumb: booleans and small enums (Mobile, color scheme, ECT) vary cleanly, yielding two to four variants. Continuous values (Viewport-Width, RTT) explode cache cardinality; bucket them in application logic before letting them near a cache key.
Delegating hints to third parties
By default, granted hints go to your origin only. Cross-origin subresources (your image CDN, say) receive them only if you delegate explicitly with Permissions-Policy:
Permissions-Policy: ch-dpr=("https://img.example"), ch-viewport-width=("https://img.example") Each hint has a policy token (ch-ua-arch, ch-device-memory, …). Delegation is a privacy decision, not plumbing: you are extending your user's data to another party, so delegate named hints to named origins and resist wildcards. In HTML, <meta http-equiv="delegate-ch"> covers the no-server-config case.
Deployment checklist
- Serve
Accept-CHon every HTML response, over HTTPS, listing only hints you consume. - Remember the list replaces the stored grant; deploys that drop the header revoke it.
- Add
Varyfor every hint that changes a cacheable response. - Reserve
Critical-CHfor render-critical hints; it costs first-visit latency. - Delegate to third-party origins explicitly via
Permissions-Policy. - Keep a no-hints fallback path, because Firefox, Safari and every bot will use it.
Next: User-Agent reduction: what the old string no longer tells you.