← Blog
Web DevelopmentApril 29, 20259 min

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

Why 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:

  1. Mosaic was first. Netscape called itself Mozilla/1.0.
  2. Sites served frames only to "Mozilla" browsers — MSIE pretended to be Mozilla/2.0 to get frames.
  3. Sites served JavaScript only to "Gecko" browsers — WebKit/KHTML browsers added (KHTML, like Gecko).
  4. 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 detection

Common Patterns Parsers Look For

  • Chrome: Chrome/ but not Chromium/ or OPR/ (Opera) or Edg/ (Edge)
  • Firefox: Firefox/ without Seamonkey/
  • Safari: Safari/ without Chrome/
  • Edge (Chromium): Edg/ (not Edge/ which is legacy)
  • Opera: OPR/
  • Mobile: Mobile in 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 →