What a User-Agent string contains
Every HTTP request your browser sends includes a User-Agent header. The value is a compressed identification string that combines the browser name and version, the rendering engine, the operating system, the device type, and a string of historical baggage going back to 1993. A modern Chrome on macOS string looks like this:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
The Mozilla/5.0 prefix is a historical artifact — every browser lies about being Mozilla because some 1990s websites refused to serve to non-Mozilla browsers. The (KHTML, like Gecko) is the same kind of lie: WebKit was based on KHTML, so it claims compatibility. Safari/537.36 at the end is yet another compatibility claim. Only the Chrome/120.0.0.0 part identifies the actual browser. This is why parsing User-Agent reliably requires a library with hundreds of regex patterns — the format isn't really a format, it's a series of historical compromises.
When parsing is useful (and when it isn't)
The right reasons to parse a UA: distinguishing bots from humans in analytics, generating compatibility-related fallbacks (most often "mobile or not"), and producing readable strings in admin dashboards for support purposes. The wrong reasons: feature detection (use 'feature' in window instead, or check capabilities directly) and security checks (UA can be spoofed trivially with a one-line setting in any browser dev tools).
Major browsers are moving toward UA Client Hints as the preferred mechanism: structured HTTP headers like Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform. Chrome already sends them by default. Server code that needs reliable detection should prefer these structured hints when available and fall back to UA parsing only when they're missing.
Common parsing gotchas
- Edge claims to be Chrome. The Edge UA contains both
Chrome/120.0.0.0ANDEdg/120.0.0.0. Naive regex that matches "Chrome" first will misidentify Edge as Chrome. Modern Brave, Opera, Vivaldi, and Samsung Internet do the same thing. - iPad masquerades as Mac since iPadOS 13. The iPad UA on Safari is identical to a Mac UA — by design, Apple decided "desktop-class browsing" should be the default. To detect iPads now, check for touch capability separately or use the
Sec-CH-UA-Platformclient hint. - Privacy browsers and extensions modify or freeze the UA. Tor Browser, Firefox's privacy.resistFingerprinting mode, and Brave's fingerprint protection all return a generic UA. Parsing it gives you a "Firefox on Windows" answer regardless of the actual browser/OS.
- "Mobile" doesn't mean small screen. A foldable phone unfolded into tablet mode reports as mobile but has 2000px width. A modern tablet may report as desktop. Use CSS media queries or
window.innerWidthfor layout decisions, not UA parsing. - Bots can be polite or impolite. Polite bots include a clear identifier (
Googlebot/2.1,bingbot/2.0,GPTBot/1.0) and a URL pointing to documentation. Impolite bots use UA strings stolen from real browsers to bypass anti-bot measures. UA parsing catches the polite ones; the impolite ones need behavioral detection.
Practical use cases
- Analytics segmentation. Group sessions by browser family for engagement metrics, but use device-pixel-ratio and viewport size separately for layout-related analysis.
- Support tickets. Showing "user reported issue from Chrome 120 on Windows 10" in your support dashboard helps reproduce bugs faster than a raw UA string.
- Bot filtering in logs. Strip out crawler traffic (Googlebot, Bingbot, AhrefsBot, SemrushBot, GPTBot, ClaudeBot) before computing real user metrics. Most are honest about themselves in the UA.
- Feature flag fallbacks. If a CSS feature you depend on (
:has(),aspect-ratio, View Transitions API) is missing in a specific old browser, you might serve a fallback layout. Prefer@supportsCSS-side over UA detection JS-side wherever possible. - Targeted content. "Download our iOS app" buttons hide on Android phones. A mobile-only UI tweak ships only to mobile UAs. These are legitimate UA use cases, though Client Hints are still preferred.
Common use cases
- Identify the browser/OS behind a support ticket
- Filter bot traffic from analytics
- Debug device-specific issues
- Audit who's crawling your site
Frequently asked questions
Can User-Agent strings be trusted for security checks?
No. Any browser dev tool or HTTP client can set User-Agent to anything. Use UA for analytics, support workflows, and content negotiation — never for authorization or security decisions.
Why does Edge claim to be Chrome?
Modern Edge's UA contains <code>Chrome/120</code> AND <code>Edg/120</code>. The Chrome part is for compatibility with sites that check for Chrome specifically. Parsers correctly identify it as Edge by looking for the Edg/ token last.
Why does iPad look like a Mac?
Since iPadOS 13, Apple deliberately uses a Mac-identical UA on Safari to enable "desktop-class browsing". To detect iPads now, check for touch capability or use the <code>Sec-CH-UA-Platform</code> client hint header instead.
What about UA Client Hints?
They're the modern replacement: structured HTTP headers like <code>Sec-CH-UA</code>, <code>Sec-CH-UA-Mobile</code>, <code>Sec-CH-UA-Platform</code>. Chrome sends them by default. Server code that needs reliable detection should prefer them over UA parsing.