How-To & Life · Guide · Developer Utilities
How to parse user agents
UA string anatomy, why they lie, Client Hints as the future, feature detection > UA sniffing, and when UA parsing actually matters.
The User-Agent header is a 30-year-old game of telephone. What started as a short self-identification from Mosaic (NCSA_Mosaic/2.0) turned into today’s unreadable Mozilla-compatible mess because every browser wanted to be served content meant for the browser that came before it. Parsing UA strings reliably is still useful for logging and feature compatibility, but the landscape has shifted under it: Chrome and Edge have frozen most of the UA string behind User-Agent Client Hints, Safari sends a deliberately reduced UA on iOS, and privacy browsers lie on purpose. This guide covers the anatomy of a UA string, why almost every browser pretends to be Mozilla, how to extract browser/OS/device cleanly, how spoofed and randomized UAs change the game, and the Client Hints API that is quietly replacing UA parsing entirely.
Advertisement
Anatomy of a UA string
A representative modern UA string:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
The grammar is loosely product/version (comment) product/version .... Tokens separated by spaces; parenthetical comments carry platform details. Order is partially standardized by RFC 9110 section 10.1.5, but every browser has historical baggage.
Why everyone claims to be Mozilla
In 1993, Netscape Navigator identified as Mozilla/1.0. Sites checked for “Mozilla” to serve enhanced content — frames, images, later JavaScript. When Internet Explorer launched in 1995, Microsoft copied the Mozilla identifier (Mozilla/1.22 (compatible; MSIE 2.0; ...)) so sites would serve IE the same content. Every browser since has played the same trick: Safari’s WebKit claims compatibility with KHTML; Chrome claims compatibility with Safari; Opera claimed compatibility with everything.
The upshot: the leading Mozilla/5.0 token identifies nothing. Skip it and read the tokens further right.
Extracting browser, OS, and device
The modern breakdown of the Chrome UA above:
Browser: the last meaningful product/version token that is not AppleWebKit, KHTML, or Safari. For Chrome: Chrome/125.0.0.0.
Engine: AppleWebKit/537.36 in the middle.
OS: inside the first parenthetical. Windows NT 10.0 maps to Windows 10/11 (both use the same NT version).
Device: in the comment on mobile UAs: iPhone, SM-G991B (a Samsung model), Pixel 8.
Common parsing libraries — ua-parser-js, Python’s user_agents, Java’s uap-core — handle the edge cases with regex sets updated regularly. Roll your own only for logging, never for feature detection.
Frozen UAs and UA reduction
In 2021, Google announced and then started shipping User-Agent reduction: Chrome pins most UA fields to static values. As of 2024, the reduced Chrome UA on desktop looks like:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
Every Chrome user on Windows 10 and Windows 11and Windows 8.1 now sends the same Windows NT 10.0 string. The device information is gone, the minor version is frozen at .0.0.0, and the real values are only available via User-Agent Client Hints.
Safari on iOS has always under-reported: iPad Safari sends a desktop UA by default. Firefox on Linux randomizes a few bits in private browsing. Treat UA strings as rough approximation, not identification.
User-Agent Client Hints
Client Hints split the UA into many small HTTP headers, each of which the site must opt into requesting.
Sec-CH-UA: "Chromium";v="125", "Not.A/Brand";v="24" Sec-CH-UA-Mobile: ?0 Sec-CH-UA-Platform: "Windows"
These three are sent on every request by default. Higher-entropy hints (full browser version, model, architecture, platform version) require the site to send Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model in an earlier response. On the next request the browser includes them.
Client-side, read them via the navigator.userAgentData API. navigator.userAgentData.getHighEntropyValues returns a promise resolving to an object with the requested fields.
Spoofed UAs
Three populations lie in their UA:
Privacy browsers: Brave, Tor Browser, and privacy add-ons deliberately randomize or generalize the UA to resist fingerprinting.
Power users: Chrome, Edge, and Firefox all have dev-tools options to send a custom UA, commonly used for testing mobile sites or bypassing UA-gated paywalls.
Bots and scrapers: good ones identify honestly (Googlebot/2.1, bingbot/2.0). Bad ones mimic Chrome or Safari exactly to blend in.
Never use UA parsing for security decisions. Rate limiting, CAPTCHA, and behavioral analysis are the right tools.
Bot identification patterns
Legitimate crawlers follow a predictable pattern: Name/Version (+URL).
Googlebot/2.1 (+http://www.google.com/bot.html) bingbot/2.0 (+http://www.bing.com/bingbot.htm) DuckDuckBot/1.1; (+http://duckduckgo.com/duckduckbot.html) facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php) Slackbot 1.0 (+https://api.slack.com/robots)
The +URL convention is a strong hint a UA is a bot. Verify a claim of being Googlebot by reverse-DNS: the request’s IP should resolve to *.googlebot.com or *.google.com, and forward-resolving that hostname should return the same IP.
UA on mobile
iPhone (iOS 17, Safari):
Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1
Android Chrome:
Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UQ1A.240205.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Mobile Safari/537.36
The model (Pixel 8) appears on Android but not on reduced-UA Chrome for desktop. iPad Safari sends a desktop UA indistinguishable from macOS Safari — detect iPad via navigator.maxTouchPoints > 1 and Mac in UA.
Common mistakes
Feature-detecting via UA string. Use if ('IntersectionObserver' in window), not if (chromeVersion > 50). The former survives the next UA reduction; the latter breaks every release.
Matching on “Chrome” to identify Chrome. Edge, Opera, Brave, Arc, and Samsung Internet all include the Chrome token. Check for Edge, Opera, etc. first and short-circuit.
Matching on “Safari” to identify Safari. Chrome includes Safari/537.36 for historical compatibility. Check for Chrome or CriOS first.
Treating Windows NT 10.0 as Windows 10.Windows 11 still reports Windows NT 10.0. The platform version in Client Hints (Sec-CH-UA-Platform-Version) distinguishes them.
Storing parsed UA strings without re-parsing. Parser libraries update monthly to handle new browsers. A string classified as unknown in 2023 might parse cleanly today. Parse on read, not on write.
Building security gates on UA. Trivial to spoof, unreliable to detect. Use real signals.
Run the numbers
Break down any UA string into browser, OS, device, and engine with the user agent parser. Pair with the HTTP status code lookup when debugging server-side UA-gated responses, and the MIME type lookup for the Accept-header side of content negotiation.
Advertisement