| Component name | ua_client_hints |
|---|---|
| In the visitorId | yes |
| structuredId group | browser (engine and build) |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 2 |
Chromium-based browsers (Chrome, Edge, Brave, Samsung Internet and others) describe themselves in a structured way called User-Agent Client Hints: the browser brands, whether it is a phone, the operating system, and on request a few more details such as the CPU architecture. WhiskerJS keeps the parts that rarely change and leaves out version numbers. Other browsers do not offer this, and the component then says so.
What it reads
From navigator.userAgentData:
brands: the brand names, without their versions;mobileandplatform;- from one
getHighEntropyValues()call:architecture,bitness,wow64,modelandformFactors.
The same call also asks for fullVersionList and platformVersion, which go to the separate ua_versions component. One request per collection is shared by both.
How the technique works
- If
navigator.userAgentDatais not an object, the component isunavailablewithno_api. - Brands are filtered: any brand that reads
notabrandonce reduced to lower-case letters is a GREASE brand (for exampleNot_A BrandorNot)A;Brand) and is dropped. The rest are sorted by name. getHighEntropyValues([...])is called once. Booleans become1or0; form factors are sorted and joined with+.- The value is
brands=...;mobile=...;platform=...;arch=...;bitness=...;wow64=...;model=...;form=.... - If the detailed call is refused (
NotAllowedErrororSecurityError, as when a Permissions Policy blocks it), the end of the value is the stable markerhe=blocked. Another error giveshe=failed; a browser withoutgetHighEntropyValuesgiveshe=none.
Why it helps
It states the device class and architecture more reliably than the user agent string, which Chromium has reduced: mobile, the platform, arm or x86, 32 or 64 bit, the Windows-on-Windows flag, the Android model name and the form factor. Brand names separate Chrome, Edge, Brave and other Chromium builds. Without versions the value stays put across browser updates.
Stability
- Brands without versions, platform and architecture rarely change.
- A new GREASE brand in a browser update does not change the value, because GREASE brands are filtered.
- The value changes if the browser renames a brand, if the page context changes from allowed to refused for the detailed hints (the
he=blockedmarker replaces the details), or when the device or form factor changes.
Browser support and what blocks it
| Browser | What you get |
|---|---|
| Chrome desktop, Chrome Android, Edge, Brave, Samsung Internet | ok (Chromium-based) |
| Safari, every iOS browser, Firefox | unavailable / no_api by design |
| Chromium in a frame or page whose Permissions Policy refuses the detailed hints | ok ending in he=blocked |
Privacy
The low-entropy part (brands, mobile, platform) is what Chromium already sends to servers by default. The detailed hints add the architecture, bitness and, on Android, the model name. WhiskerJS requests no version numbers for this component, makes no request and shows no prompt. Values over 128 characters or with non-ASCII characters (for example an unusual model name) are hashed.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
the value, with details or a he=blocked, he=failed or he=none marker |
|
unavailable |
no_api |
no navigator.userAgentData |
unavailable |
no_digest |
the value needs hashing and there is no crypto.subtle |
excluded |
caller_excluded |
you excluded it |
timed_out |
timeout, aborted |
the detailed hints did not answer within 1000 ms, or budget or abort |
Errors of the detailed call never fail the component; they become a marker. Your server can compare the platform here with the user agent header it receives; see Recommended server checks.
Example value
From headless Chromium in a Linux container: brands=Chromium,HeadlessChrome;mobile=0;platform=Linux;arch=arm;bitness=64;wow64=0;model=;form=Desktop. Firefox and WebKit in the same run: unavailable / no_api. A desktop Chrome on a Mac looks like brands=Chromium,Google Chrome;mobile=0;platform=macOS;arch=arm;bitness=64;wow64=0;model=;form=Desktop.
Questions and answers
What is the GREASE brand and why is it removed?
Chromium adds a deliberately odd brand such as Not_A Brand that changes between versions, so that sites do not depend on an exact brand list. WhiskerJS drops any brand that reads notabrand once reduced to letters, so the id does not change with it.
Does getHighEntropyValues prompt the user?
No. It is a promise-based read with no prompt and no network request. A page or frame can be refused by a Permissions Policy, which WhiskerJS reports with the stable marker he=blocked.
Why are version numbers not in this component?
They change with every browser and operating system update. They are reported separately in ua_versions, which is outside both ids.
Why is it unavailable in Safari and Firefox?
Only Chromium-based browsers implement navigator.userAgentData. Elsewhere the component is unavailable with ruleId no_api, by design.