# getHighEntropyValues example: what a page asks and gets

> getHighEntropyValues hands a page architecture, bitness, model and full versions on request, and each answer must match the headers Chrome sends.

Source: https://mosaicbrowser.com/blog/gethighentropyvalues-explained

## getHighEntropyValues: what a page can ask for and what it gets

getHighEntropyValues hands a page architecture, bitness, model and full versions on request, and each answer must match the headers Chrome sends.

![Asked for in script, sent in the headers, beside a screenshot: a profile fingerprint in Mosaic: GPU, screen, fonts, timezone and language matched to the exit](https://mosaicbrowser.com/covers/gethighentropyvalues-explained.webp)

`navigator.userAgentData.getHighEntropyValues()` takes a list of hint names and returns a promise with the answers. The low-entropy hints, `brands`, `mobile` and `platform`, come back whatever you ask for, because Chrome already sends them to every site. The high-entropy ones (architecture, bitness, model, platform version, full versions) come back only when named. Whatever the script gets must agree with the `Sec-CH-UA-*` headers the same browser sends.

## A getHighEntropyValues example

Paste this into the DevTools console on any HTTPS page:

```
navigator.userAgentData
  .getHighEntropyValues([
    'architecture', 'bitness', 'model',
    'platformVersion', 'fullVersionList', 'wow64'
  ])
  .then((ua) => console.log(JSON.stringify(ua, null, 2)))

```

On a Chrome on an Apple Silicon Mac the shape of the answer is this (the versions are yours, not these):

```
{
  "architecture": "arm",
  "bitness": "64",
  "brands": [ ... three entries, majors only ... ],
  "fullVersionList": [ ... the same three, four-part versions ... ],
  "mobile": false,
  "model": "",
  "platform": "macOS",
  "platformVersion": "<the macOS version>",
  "wow64": false
}

```

Three things in it surprise people. `model` is an empty string on every desktop, because Chromium fills it only on an Android build. `architecture` on an Apple Silicon Mac is `arm`, while the User-Agent string beside it still says `Intel Mac OS X 10_15_7`, the frozen value every Mac Chrome sends. And there is no prompt: the promise just resolves. If you only want the low-entropy half, `navigator.userAgentData.toJSON()` gives it to you synchronously.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## Low entropy by default, high entropy on request

User-Agent Client Hints split what the old User-Agent string said into pieces and made most of them opt-in. Three pieces go out with every request on a secure origin, as headers:

```
Sec-CH-UA            the brand list, majors only
Sec-CH-UA-Mobile     ?0 or ?1
Sec-CH-UA-Platform   "macOS", "Windows", "Android" ...

```

The rest a site has to ask for. A server asks with a response header, `Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List`, and the browser attaches those headers to later requests to that origin. A script asks with `getHighEntropyValues()`. A cross-origin iframe gets the high-entropy answers only if the top page delegates them through `Permissions-Policy`, which is how the API keeps a third-party frame from collecting everything by default.

That is the whole privacy model: nothing is hidden, it is just not volunteered. A site that wants your platform version can have it for one line of script. What the split changed is that asking is now visible to anyone who looks, in the response headers and in the call.

## Two readers, one browser

Every high-entropy hint has two ways out of the browser, and a detector reads both. The script answer and the request header come from one structure inside Chromium, the user-agent metadata, so in a real Chrome they cannot disagree. `getHighEntropyValues(['platformVersion'])` and `Sec-CH-UA-Platform-Version` print the same string. `fullVersionList` and `Sec-CH-UA-Full-Version-List` list the same builds. A dedicated worker, a shared worker and a service worker each have their own `navigator.userAgentData`, and they answer the same as the page.

So the check a detector runs is cheap. Read the hints in script, read the headers on the server, read them again from a worker, and compare. A tool that changes the answer in one place and not the others gives itself away in a single request, and no amount of care about the values themselves helps, because the values were never the problem.

The pairs also have to make sense on their own terms. An empty `bitness` beside a filled-in `architecture` is not something any browser sends. A desktop `platform` beside a non-empty `model` is a phone's field on a computer. A full version with fewer than four parts is a build no Chrome has shipped ([our engine once reported a bare `"153"`](https://mosaicbrowser.com/blog/full-version-without-build-number)).

## What happens when you change the User-Agent

The common way to get this wrong is to launch Chromium with `--user-agent`. We measured it in a stock Chrome, with that as the only flag and a string byte-identical to Chrome's own default:

```
without --user-agent   architecture "arm"   platformVersion "26.6.2"
with    --user-agent   architecture ""      platformVersion ""

```

Every high-entropy hint, `architecture`, `bitness`, `platformVersion`, `uaFullVersion` and `fullVersionList`, came back empty. Chromium's client-hints code has a branch for exactly this case, named `disable_due_to_custom_ua`: once the User-Agent is one Chromium did not build, it stops vouching for the values it would have derived from it. No real browser answers that way, and IPHey read it as an inconsistent fingerprint. How that flag ended up sent on some launches and not others is a story of its own, in [a fingerprint that depended on launch order](https://mosaicbrowser.com/blog/user-agent-launch-order).

## How Mosaic sets the hints

Tesseract Chrome, Mosaic's engine, moves these values in C++. Our patch corrects the user-agent metadata at the point where Chromium builds the request headers and at the point where it builds the script's answer, from the same switches, so a page, its workers and the server see one browser. A desktop profile's `model` stays the empty string desktop Chrome sends. A mobile profile carries the model of the phone it was captured from, `SM-S918U`, `moto g05` or `SM-S936U`, beside the empty architecture and bitness those phones report. And we pass `--user-agent` only when a profile wants a different string from the engine's own or is started headless, because the hints go empty when we do.

The GREASE entry in `brands`, the odd-looking `Not_A Brand` with its own version, is built by Chromium from the major version and should not be "fixed" ([where its characters come from](https://mosaicbrowser.com/blog/not-a-brand-grease)). The other values a page can read beside these are listed in the [browser fingerprint glossary entry](https://mosaicbrowser.com/glossary/browser-fingerprint).

## Questions

### What does getHighEntropyValues return?

A promise that resolves to an object with the hints you asked for, such as architecture, bitness, model, platformVersion and fullVersionList, plus the low-entropy ones (brands, mobile, platform), which are always included.

### Does getHighEntropyValues show a permission prompt?

No. A top-level page gets the answer without asking the user. A cross-origin iframe gets the high-entropy values only if the page delegates them through Permissions-Policy.

### Does getHighEntropyValues work in Firefox and Safari?

No. navigator.userAgentData is a Chromium API, so it exists in Chrome, Edge and other Chromium browsers and is undefined in Firefox and Safari.

### Why is architecture an empty string in my Chrome?

On Android an empty architecture and bitness are normal. On a desktop, one cause we measured is launching Chrome with --user-agent: Chromium then returns every high-entropy hint empty, because it will not vouch for values it cannot derive from a User-Agent it did not build.

## Related

[A full Chrome version with no build number is one no Chrome shipped](https://mosaicbrowser.com/blog/full-version-without-build-number): Our engine reported uaFullVersion as "153". Real Chrome always sends four numbers. Sending the full build moved FingerprintJS's confidence to stock's 0.99.

[A fingerprint that depended on launch order](https://mosaicbrowser.com/blog/user-agent-launch-order): Chrome's --user-agent flag left Sec-CH-UA-Bitness empty, and a cache empty at every app start decided when we sent it. One profile wore two identities.

[Not_A Brand: how Chrome's GREASE brand is computed](https://mosaicbrowser.com/blog/not-a-brand-grease): The Not A Brand entry in Chrome's user agent is computed from the major version by a fixed formula, so a label that doesn't fit the claimed major is a tell.

[Browser fingerprint: what a site can read about your machine](https://mosaicbrowser.com/glossary/browser-fingerprint): A browser fingerprint is the set of values a site reads about your browser and computer, which together recognize you without cookies. What goes into one.

[Why every Mac's Chrome says Mac OS X 10_15_7](https://mosaicbrowser.com/blog/mac-os-x-10-15-7): Chrome froze the macOS version in its User-Agent, so every Mac claims 10.15.7. The real one is in Sec-CH-UA-Platform-Version, a hint sites must ask for.

[Why the User-Agent says Windows 10 on Windows 11](https://mosaicbrowser.com/blog/windows-11-user-agent-windows-10): Windows 11 and 10 both send Windows NT 10.0 in the User-Agent. Only the platformVersion client hint, 13 or higher, says Windows 11.

[Why Chrome's User-Agent says 0.0.0](https://mosaicbrowser.com/blog/chrome-user-agent-0-0-0): Chrome zeroes everything after the major, so the User-Agent says 0.0.0 and the build moves to Sec-CH-UA-Full-Version-List. A real build in the UA is a tell.

[Privacy Sandbox is retired, and its APIs are a version tell](https://mosaicbrowser.com/blog/privacy-sandbox-apis-removed): Chrome is removing Topics and Protected Audience release by release, so whether runAdAuction exists tells a page which Chrome you really are.

## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
