Sec-CH-UA and User-Agent Client Hints explained (tested in Chromium)

User-Agent Client Hints are the Sec-CH-UA* request headers Chromium-based browsers send alongside (and eventually instead of) the User-Agent string. Three are sent by default; the rest only after the server asks with Accept-CH. We logged every one on a test server to see exactly when each appears.

1. What Chromium sends by default

First visit to http://localhost:8965/ in a fresh profile, with no Accept-CH anywhere. These three low-entropy hints arrived on the navigation, on fetch(), on an <img> and on the favicon request:

sec-ch-ua: "Chromium";v="153", "Not_A Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Linux"
user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/153.0.0.0 Safari/537.36

2. Asking for more with Accept-CH

The server answered a page with:

Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Arch, Sec-CH-UA-Model
RequestHigh-entropy hints received?
The navigation whose response carried Accept-CHNo, only the default three
fetch(), <img>, favicon from that pageYes, all four
Next navigation on the same originYes
A later navigation after a page without Accept-CHYes (the preference was remembered)
http://127.0.0.1:8965 (different origin)No, only the default three
Cross-origin fetch() to 127.0.0.1 from that pageNo, only the default three

Values received on Linux x86_64:

sec-ch-ua-full-version-list: "Chromium";v="153.0.8010.12", "Not_A Brand";v="8.0.0.0"
sec-ch-ua-arch: "x86"
sec-ch-ua-platform-version: ""
sec-ch-ua-model: ""

Note the empty strings: platform version was blank on Linux and model is blank on desktop. Code that reads these must handle "". A <meta http-equiv="Accept-CH" content="Sec-CH-UA-Arch"> tag behaved differently from the header: the page's own fetch() got sec-ch-ua-arch, but the next navigation did not, so the meta tag is not remembered.

3. Critical-CH: getting hints on the very first request

Adding Critical-CH: Sec-CH-UA-Platform-Version next to Accept-CH made Chromium retry the same navigation: our server logged 2 hits for one page.goto(), the first with the default three hints, the second with sec-ch-ua-platform-version and sec-ch-ua-full-version-list. The next navigation was a single hit carrying both hints. Use it only when the first response truly depends on the hint; it costs a full extra round trip on first visit.

4. Third-party requests need Permissions-Policy

With only Accept-CH, the cross-origin request to 127.0.0.1 got the default three hints. Adding

Permissions-Policy: ch-ua-arch=(self "http://127.0.0.1:8966"), ch-ua-full-version-list=(self "http://127.0.0.1:8966")

to the page response made the same cross-origin fetch() carry sec-ch-ua-arch and sec-ch-ua-full-version-list.

5. Secure contexts only

OriginisSecureContextSec-CH-UA headersnavigator.userAgentData
http://localhost:8965trueYes, Accept-CH honouredpresent
http://127.0.0.1:8965trueYes, Accept-CH honouredpresent
http://plain.test:8965 (mapped to 127.0.0.1)falseNone, even after Accept-CHundefined

So hints work on local dev but disappear on a plain-http staging host. Serve over HTTPS if your code depends on them. curl 8.14.1 sent only host, user-agent: curl/8.14.1 and accept: */*, and Node.js 24 fetch() sent no sec-ch-* headers either: non-browser clients send none.

6. The JavaScript side

navigator.userAgentData.brands
// [{brand:"Chromium",version:"153"},{brand:"Not_A Brand",version:"8"}]
await navigator.userAgentData.getHighEntropyValues(
  ["architecture","bitness","model","platformVersion","fullVersionList","wow64","formFactors"])
// architecture:"x86", bitness:"64", model:"", platformVersion:"",
// fullVersionList:[{brand:"Chromium",version:"153.0.8010.12"},{brand:"Not_A Brand",version:"8.0.0.0"}],
// wow64:false, formFactors:["Desktop"], mobile:false, platform:"Linux", uaFullVersion:"153.0.8010.12"

No Accept-CH was needed for the JavaScript API. If your response changes based on any hint, add it to Vary (for example Vary: Sec-CH-UA-Mobile) so caches keep the variants apart; see Vary and caching. Related request headers: Sec-Fetch-Site, Mode and Dest and Accept-Language.

Frequently asked questions

What is the sec-ch-ua header?

A User-Agent Client Hint listing the browser's brands with major versions. Chromium 153 sent "Chromium";v="153", "Not_A Brand";v="8" by default on every request to a secure origin.

What does Not_A Brand mean in sec-ch-ua?

It is GREASE: an intentionally fake brand entry so servers parse the list instead of matching an exact string. Its name and version can change, so ignore it and look for the brand you care about.

Why is Sec-CH-UA-Platform-Version not sent?

It is a high-entropy hint: the server must request it with Accept-CH first. In Chromium 153 the response carrying Accept-CH did not get it, the following requests did. Add Critical-CH to make the browser retry the first navigation with it. On Linux its value was an empty string.

Why are client hints missing on my site?

They are only sent to secure contexts. localhost and 127.0.0.1 over http got them, but a plain-http host name mapped to 127.0.0.1 got no Sec-CH-UA headers at all, even after Accept-CH, and navigator.userAgentData was undefined.

Are client hints sent to third-party requests?

Only the default three. A cross-origin fetch got sec-ch-ua-arch and sec-ch-ua-full-version-list only after the page delegated them with Permissions-Policy: ch-ua-arch=(self "origin").

Does curl send sec-ch-ua?

No. curl 8.14.1 sent only Host, User-Agent and Accept, and Node.js 24 fetch sent no sec-ch-* headers. Client hints come from browsers.

Written with AI assistance. Measured on 2026-10-01 in headless Chromium 153.0.8010.12 on Linux x86_64 (Playwright, channel 'chromium', fresh context per test) against a Node.js 24.21.0 http server on localhost:8965-8966 that logged every Sec-CH-UA* header and counted hits. 127.0.0.1 and a host name mapped to 127.0.0.1 with --host-resolver-rules (plain http, not localhost) were used to test secure-context rules. curl 8.14.1 and Node.js fetch were logged by the same server. Other browsers were not tested. PlainHeaders is our extension; DevTools → Network → Headers shows the same request headers, free.