guide 3 of 3
User-Agent reduction
Chrome did not remove the User-Agent string. It did something sneakier. The header still arrives, still looks plausible, and several of its fields are now permanently frozen lies.
What actually changed
Between Chrome 101 and 110 (2022–2023), Chromium progressively replaced the variable parts of the UA string with fixed tokens. Compare a real Android request, before and after:
# before reduction
User-Agent: Mozilla/5.0 (Linux; Android 13; Pixel 7 Pro) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/109.0.5414.85 Mobile Safari/537.36
# after reduction, same phone, today
User-Agent: Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36
(KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36 Every difference is a freeze:
Android 10: every Android reports 10, forever, regardless of reality.K: every device model is the letter K. A Pixel, a Galaxy Fold, a TV: K.126.0.0.0: the major version is real; minor, build and patch are zeroed.- On desktop,
Windows NT 10.0; Win64; x64is frozen (Windows 11 says NT 10.0) and macOS is pinned at10_15_7on every modern Mac.
The string parses fine. That is the trap: no errors, no nulls, just wrong answers, which is how OS-version dashboards drifted for months before anyone noticed. Safari did the same in spirit years earlier (frozen at Mac OS X 10_15_7), and Firefox caps its reported versions similarly. Reduction is the industry direction, not a Chrome quirk.
The migration map
Everything removed has a designated replacement:
| You used to parse | Now frozen to | Ask instead for |
|---|---|---|
| Android version | Android 10 | Sec-CH-UA-Platform-Version |
| Device model | K | Sec-CH-UA-Model |
| Full Chrome version | 126.0.0.0 | Sec-CH-UA-Full-Version-List |
| Windows / macOS version | NT 10.0 / 10_15_7 | Sec-CH-UA-Platform-Version |
| CPU / bitness tokens | Win64; x64 (frozen) | Sec-CH-UA-Arch + Sec-CH-UA-Bitness |
| Mobile vs desktop | Mobile token (still real) | Sec-CH-UA-Mobile |
Each replacement is opt-in via Accept-CH, which is the entire point: the data still exists, but requesting it is now a visible act instead of ambient collection.
Who this bites, in practice
- Analytics pipelines. OS-version and device-model charts silently became fiction in 2023 unless the collector migrated to hints or a hint-aware parsing service.
- Compatibility gates. "This OS version is unsupported" checks keyed on the UA string now misfire; a Windows 11 machine introduces itself as NT 10.0.
- Fraud and bot detection. Coarser UA strings collapse device diversity, so legitimate-vs-spoofed analysis leans harder on consistency checks between the UA string, the hints, and network-level signals like the requesting IP's reputation.
The two-source reality
After reduction, neither source is sufficient alone. Hints are precise but Chromium-only; the UA string is universal but partially frozen. Production detection in 2026 reads both:
// simplified server-side logic
const brands = parseStructuredList(req.headers['sec-ch-ua']);
if (brands) {
// Chromium: trust the hints, request more via Accept-CH if needed
return detectFromHints(req.headers);
}
// Firefox, Safari, bots, HTTP libraries: parse the classic string
return parseUserAgent(req.headers['user-agent']); The second branch is the one that hurts to maintain, because UA parsing is an arms race of GREASE tricks, frozen tokens and new devices. Delegating it to a service is a reasonable trade: Ipregistry's user-agent endpoint keeps the parser current on the server side, returns structured browser/engine/OS/device fields, and folds in the IP-level context (geolocation, VPN and threat signals) that UA data alone cannot provide.