# Chrome's --user-agent flag and the client hints it breaks

> 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.

Source: https://mosaicbrowser.com/blog/user-agent-launch-order

## A fingerprint that depended on 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.

![The first launch of a session wore a different identity](https://mosaicbrowser.com/covers/user-agent-launch-order.webp)

Chrome's `--user-agent` flag changes the User-Agent string and leaves the client hints half-done, so on its own it is a fingerprint rather than a disguise. We measured exactly that, and then found something worse: whether our launcher sent the flag at all depended on a cache that was empty at every app start. The first profile launched in each session wore one identity, and every later launch of the same profile wore another.

## Two runs, two browsers

The report was a Pixelscan screenshot: "Your Browser Fingerprint is inconsistent", with the browser card red and reading Edge 153.0.8010.37 on Windows. A second run of the same profile read Edge 153.0.0.0. One profile, two answers, which is how every report that arrives as "sometimes" looks.

We read the headers off a local server, launching the same Edge profile twice:

```
without --user-agent   UA  Edg/153.0.8010.37   Sec-CH-UA-Bitness "64"
with    --user-agent   UA  Edg/153.0.0.0       Sec-CH-UA-Bitness ""

```

Neither row is a real browser. User-Agent reduction pins everything after the major version to zero, so no Edge release has put a build number in its User-Agent. And no browser sends an empty `Sec-CH-UA-Bitness` beside a filled-in `Sec-CH-UA-Arch`. That empty value is Chromium declining to vouch for high-entropy hints it cannot derive from a User-Agent it did not build.

So the two choices on offer were a wrong User-Agent or a wrong bitness hint.

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)

## What --user-agent does and does not move

If you launch Chrome with `--user-agent="..."`, the `User-Agent` header and `navigator.userAgent` change. Everything else that describes the same browser stays where it was or goes blank: the brand list in `navigator.userAgentData`, the `Sec-CH-UA` headers, and the high-entropy hints a site asks for, such as platform version, architecture and bitness.

A site that compares those does not need to know the real value. It needs two answers that disagree, and the flag supplies them. That is the general problem with changing a fingerprint from outside the engine, whether by flag or by page script, which we wrote about in [C++, not JavaScript](https://mosaicbrowser.com/blog/cpp-not-javascript).

## Why it was intermittent

Our launcher passed `--user-agent` only when the profile's string differed from the engine's own. The engine's own came from an in-memory `Map`, filled on first use and empty at every app start. Measured on one profile, three launches after restarting Mosaic:

```
launch 1   Edg/153.0.8010.37
launch 2   Edg/153.0.0.0
launch 3   Edg/153.0.0.0

```

The first launch found the cache empty and skipped the flag. Every later launch found it full and sent it. A profile that presents two identities is worse than either one, because a real browser does not change its build number between two visits on the same day.

We now state the rule plainly in our own notes: a flag whose presence depends on a cache is a fingerprint that depends on launch order.

## One switch meaning two things

Underneath the cache was the real cause. Our engine takes a `--fingerprint-brand-version` switch, and it meant two things. It was the only source for the client-hint version lists, which need the full build, `153.0.8010.37`. And the code that builds the Edge suffix used the same value verbatim in the User-Agent, which needs `153.0.0.0`.

The patch already knew both shapes. It carried the reduced version for Edge's User-Agent and used it, but only when the switch was absent. Supplying the switch, which we must for the hints, took the other path.

Chrome never showed the fault because Chrome has no suffix. For a Chrome profile, the engine builds the User-Agent byte for byte from the platform the profile claims, with no `--user-agent` at all. Measured on a fresh Windows Chrome profile, three launches from a cold start: the User-Agent matched the stored string every time, and `Sec-CH-UA-Bitness` was `"64"` every time.

## What we changed

Edge is refused. It had been drawn for 14% of Windows profiles; now none are, and asking for it by name gets an error that quotes both measurements rather than a bare list of allowed brands. A check in our build, `check:brand`, keeps it that way.

The patch is fixed for later engine builds: a build number is cut down to the reduced form before it reaches the suffix, so one switch can serve both shapes. Edge stays refused until an engine built from that fix has been measured.

The Pixelscan report also said "No proxy detected", because the run came from a home address. That half was not a bug. Half of what a consistency scanner checks is whether the exit agrees with the timezone and language, so a profile measured without its proxy is being graded on the wrong address.

![A new Mosaic profile with its proxy checked and the fingerprint drawn for that exit](https://mosaicbrowser.com/shots/profile-editor.png)
*A profile is drawn for its exit, then launched*

How the browser identity itself is drawn, from real hardware records rather than typed strings, is on [real fingerprints](https://mosaicbrowser.com/features/real-fingerprints). The control we now run first, before blaming our own engine for any verdict, is in [the control that settles "is it our patches?"](https://mosaicbrowser.com/blog/the-control-is-googles-build).

## Questions

### Does the --user-agent flag change client hints in Chrome?

Not consistently. In our measurement a Chromium launched with --user-agent still sent Sec-CH-UA-Arch but sent an empty Sec-CH-UA-Bitness, a pair no real browser sends. Chromium will not vouch for high-entropy hints it cannot derive from a User-Agent it did not build.

### Is changing the User-Agent enough to change a browser fingerprint?

No. The User-Agent is one of several places the same claim appears: navigator.userAgent, navigator.userAgentData, the Sec-CH-UA request headers and the high-entropy hints. A site compares them, so they have to be moved together.

### Why does Mosaic refuse the Edge brand?

Because one engine switch fed both Edge's client-hint version list, which needs the full build number, and its User-Agent suffix, which needs the reduced major.0.0.0 form. Our engine could give Edge a correct User-Agent or a correct bitness hint, never both.

### How does Mosaic set the User-Agent for a Chrome profile?

The engine builds the User-Agent and every client hint from the platform the profile claims, so for a Chrome profile the string already matches and --user-agent is not needed. One source feeds all of them.

## Related

[The control that settles "is it our patches?"](https://mosaicbrowser.com/blog/the-control-is-googles-build): Unpatched Chrome for Testing got the same IPHey verdict as our engine, so our patches were not the cause. Its voice list still differs from Chrome's.

[Pixelscan calls a stock Chrome masked 3 times in 20](https://mosaicbrowser.com/blog/pixelscan-masks-stock-chrome): A Pixelscan false positive, measured: an untouched Google Chrome read 'Masking detected' on 3 of 20 runs. Our fresh profiles read it once in 74.

[Why we change fingerprints in C++, not JavaScript](https://mosaicbrowser.com/blog/cpp-not-javascript): A JavaScript spoof is one realm deep. An iframe read the real source of 33 of our patched functions; the same values changed in C++ agree everywhere.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.









## 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.
