User-Agent Strings: Parsing, Structure, and Feature Detection
The structure of User-Agent strings, how browsers evolved to include each other's tokens, and when to use UA detection versus feature detection.
What Is a User-Agent String?
A User-Agent (UA) string is an HTTP header sent with every browser request that identifies the client: the browser, its version, the rendering engine, and the operating system. Servers and analytics tools parse this string to tailor responses or gather statistics.
Chrome on Windows
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36Why Every Browser Claims to Be Mozilla
UA strings are famously convoluted because each new browser copied tokens from existing strings to avoid being blocked by sites that served features only to known browsers. The story:
- Mosaic was first. Netscape called itself
Mozilla/1.0. - Sites served frames only to "Mozilla" browsers — MSIE pretended to be
Mozilla/2.0to get frames. - Sites served JavaScript only to "Gecko" browsers — WebKit/KHTML browsers added
(KHTML, like Gecko). - Sites detected Chrome by looking for Safari — Chrome added
Safari/...to its UA.
The result: every modern browser UA string contains Mozilla/5.0, regardless of actual Mozilla lineage.
Anatomy of a Chrome UA String
Mozilla/5.0 ← always "5.0", historic
(Windows NT 10.0; Win64; x64) ← OS and architecture
AppleWebKit/537.36 ← rendering engine
(KHTML, like Gecko) ← compatibility token
Chrome/124.0.0.0 ← actual browser and version
Safari/537.36 ← compat token for Safari detectionCommon Patterns Parsers Look For
- Chrome:
Chrome/but notChromium/orOPR/(Opera) orEdg/(Edge) - Firefox:
Firefox/withoutSeamonkey/ - Safari:
Safari/withoutChrome/ - Edge (Chromium):
Edg/(notEdge/which is legacy) - Opera:
OPR/ - Mobile:
Mobilein the platform string
Reading the UA in JavaScript
// Legacy — synchronous, deprecated in some contexts
const ua = navigator.userAgent;
// Modern — structured, privacy-preserving
const brands = navigator.userAgentData?.brands;
// [{ brand: 'Chromium', version: '124' }, { brand: 'Google Chrome', version: '124' }]
const platform = await navigator.userAgentData?.getHighEntropyValues([
'platform', 'platformVersion', 'architecture', 'model'
]);UA Client Hints: The Modern Approach
The User-Agent Client Hints API (navigator.userAgentData) is the successor to the UA string. It provides structured data instead of a string to parse, and is privacy-preserving: only low-entropy data is sent by default; high-entropy data (exact version, architecture) requires an explicit request.
Chrome has been actively reducing the UA string since 2021 — freezing the minor version, OS version, and device model to reduce passive fingerprinting. If your code depends on these values from the UA string, it will break.
Feature Detection: The Right Alternative
For the vast majority of cases, detect capabilities rather than browsers:
// Bad: UA sniffing
if (ua.includes('Chrome')) {
useFetchAPI();
}
// Good: feature detection
if (typeof fetch !== 'undefined') {
useFetchAPI();
}UA strings are unreliable for capability detection — browsers update constantly, spoof each other, and users can modify their UA string. Feature detection is deterministic and version-agnostic.
When UA Parsing Is Legitimate
- Analytics: Understanding your user base's browser/OS distribution
- Logging: Recording what client made a request for debugging
- Bot detection: Identifying crawlers and headless browsers (though bots can spoof UAs)
- Redirecting mobile users: Serving a different asset bundle or triggering a native app redirect
Try it yourself
Decode any User-Agent string to browser, engine, OS, and device type instantly.
Open User Agent Parser →