User-Agent Strings Explained: How to Read & Generate Them

The User-Agent header explained: its Mozilla/5.0 format, legacy tokens, per-browser patterns, UA reduction and Client Hints, plus generating fictional UAs.

By FakeName Editorial TeamPublished June 25, 2026Last updated June 25, 20268 min read

Every HTTP request your browser sends carries a User-Agent header, and almost nobody reads it until something breaks: a feature flag misfires on Safari, a crawler gets blocked, or analytics misclassifies a device. This guide breaks the string down token by token, explains why it is stuffed with decades-old compatibility cruft, and shows how to generate fictional UA strings for QA and crawler testing without impersonating real people.

What is a user agent string and what is it for?

A User-Agent string is the text value of the HTTP `User-Agent` request header that identifies the software making a request, including its product name, version, layout engine, and host platform. Servers read it to adapt responses, gather analytics, and route traffic. It is client-supplied text, so it is editable and never a trustworthy proof of identity [mdn-ua-header].

The header has shipped since the earliest HTTP specifications. RFC 9110, the current HTTP Semantics standard, defines `User-Agent` as a field whose value contains "product identifiers" with optional comments, ordered by significance, and warns that overly long or detailed values can leak fingerprinting data [rfc9110]. That warning is the seed of the whole UA reduction story later in this article.

How do you read a Chrome user agent string token by token?

Read a UA string left to right as ordered layers: a `Mozilla/5.0` compatibility prefix, a parenthesized platform comment (OS and CPU), the layout engine token, and one or more product tokens for the browser and its base. Each token is `Name/Version` and the parentheses hold free-form comments. Most tokens exist for backward compatibility, not accuracy [mdn-ua-header].

Take a concrete desktop Chrome string and split it apart:

`Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36`

TokenPart of UAWhat it actually means
Mozilla/5.0Compatibility prefixLegacy token every major browser sends; signals "Mozilla-compatible" to old server sniffers. Carries no real version meaning today.
Windows NT 10.0Platform commentOS identifier. NT 10.0 covers both Windows 10 and Windows 11; the kernel version did not change between them.
Win64; x64Platform comment64-bit Windows on a 64-bit x86 CPU architecture.
AppleWebKit/537.36Engine tokenDeclares WebKit lineage. Chrome forked Blink from WebKit but keeps this token for compatibility.
(KHTML, like Gecko)Engine commentChained heritage: WebKit derived from KDE's KHTML, and "like Gecko" placates Firefox-targeted code paths.
Chrome/120.0.0.0Product tokenThe actual browser and major version. Under UA reduction the minor, build, and patch fields are frozen to 0.0.0.
Safari/537.36Product tokenTrailing Safari token kept so scripts that gate features on Safari still serve Chrome.
Token-by-token breakdown of a desktop Chrome 120 User-Agent on Windows.

The lesson: only two tokens in that line, `Chrome/120.0.0.0` and the OS comment, carry information a developer usually wants. The rest is sediment from twenty years of browsers copying each other to avoid getting served a downgraded page.

Why is the user agent full of legacy tokens like Mozilla and KHTML?

The UA string is full of legacy tokens because browsers historically copied each other's identifiers to pass server-side feature detection. In 1994, Netscape (codename Mozilla) gained frames; servers checked for the `Mozilla` token before sending frame markup, so every later browser claimed to be Mozilla-compatible to receive the modern page [history-of-ua].

The pattern compounded with each browser generation. Internet Explorer claimed `Mozilla` and added `compatible; MSIE`. Safari's engine descended from KHTML, so it kept `KHTML, like Gecko`. Chrome forked Blink from WebKit yet still ships `AppleWebKit` and `Safari` tokens. The result is a string that reads like an archaeological dig where each layer was added to satisfy code that checked for an older browser.

And then Microsoft came and made Internet Explorer and called itself Mozilla/4.0 (compatible; MSIE), and there was great rejoicing... and the parsers got so confused that they all gave up and just said everyone is Mozilla.
Aaron Andersen, paraphrasing the well-known "History of the browser user-agent string"

This is why client-side feature detection (testing for an API's existence) beats UA sniffing. MDN and the WHATWG both recommend feature detection precisely because the UA string is unreliable by design [mdn-ua-detection].

What does the user agent format look like for each major browser?

Each browser follows the same skeleton, `Mozilla/5.0 (platform) engine (engine-comment) product tokens`, but fills it with distinct markers. Chrome adds `Chrome/` and `Safari/`; Firefox adds `Gecko/` and `Firefox/`; Safari adds `Version/` plus `Safari/`; Edge appends `Edg/`. Mobile strings insert `Mobile` and device hints. Matching these patterns is how parsers classify traffic [mdn-ua-header].

Browser / platformRepresentative User-Agent stringDistinguishing token
Chrome (Windows)Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Chrome/ with trailing Safari/
Firefox (Windows)Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0rv: + Gecko/ + Firefox/
Safari (macOS)Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15Version/ before Safari/, no Chrome/
Edge (Windows)Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0Edg/ appended after Safari/
Chrome (Android phone)Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36Android + Mobile token (device frozen to K)
Safari (iPhone)Mozilla/5.0 (iPhone; CPU iPhone OS 17_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Mobile/15E148 Safari/604.1iPhone OS + Mobile/ build code
Representative User-Agent patterns per major browser (versions illustrative, late 2024 era).

How do you tell Edge and Chrome apart?

Edge is built on Chromium, so its UA is identical to Chrome's right up to the end, where it appends `Edg/<version>` (no "e" on the end). Parse from the right: if you see `Edg/`, it is Edge; if `OPR/`, Opera; if `SamsungBrowser/`, Samsung Internet. A bare `Chrome/...Safari/537.36` with no trailing vendor token is plain Chrome.

How do you detect mobile from a user agent?

Mobile UAs include a `Mobile` token (Android Chrome) or `Mobile/<build>` (iOS Safari), plus a platform marker like `Android` or `iPhone`. Note that Android Chrome now reports the device model as the literal letter `K`, a deliberate consequence of UA reduction. For reliable mobile detection, prefer the `Sec-CH-UA-Mobile` Client Hint over string matching [ua-ch-explainer].

What is User-Agent reduction and how do Client Hints replace the UA?

User-Agent reduction is Google's multi-phase program to freeze and shorten Chrome's UA string to a fixed, low-entropy template, removing minor version, patch, and detailed OS and device data. Sites that still need that information request it explicitly through User-Agent Client Hints, a set of `Sec-CH-UA-*` request headers and the `navigator.userAgentData` JavaScript API [ua-reduction].

The motivation is privacy. The high-entropy bits in a full UA string (exact patch version, OS build, device model) contribute to passive fingerprinting, the same risk RFC 9110 flags [rfc9110]. By freezing those fields and gating the rest behind opt-in hints, the browser shrinks how much a server learns by default. Chrome's reduced desktop UA freezes the platform version and zeroes the minor build numbers; the device model on Android collapses to `K`.

Client Hint headerReplaces (from old UA)Example value
Sec-CH-UABrowser brand + major version list"Chromium";v="120", "Google Chrome";v="120", "Not?A_Brand";v="24"
Sec-CH-UA-MobileMobile vs desktop flag?0 (desktop) or ?1 (mobile)
Sec-CH-UA-PlatformOS name"Windows"
Sec-CH-UA-Platform-VersionOS version (high entropy)"15.0.0"
Sec-CH-UA-ArchCPU architecture (high entropy)"x86"
Sec-CH-UA-Full-Version-ListFull browser versions (high entropy)"Google Chrome";v="120.0.6099.110"
Key User-Agent Client Hint headers and their full-UA equivalents.

Low-entropy hints (`Sec-CH-UA`, `Sec-CH-UA-Mobile`, `Sec-CH-UA-Platform`) are sent by default. High-entropy hints (architecture, full version, model, platform version) are only delivered after a server requests them via the `Accept-CH` response header, or read on demand through `navigator.userAgentData.getHighEntropyValues()` [ua-ch-explainer]. For QA, this means you must test both the legacy UA path and the Client Hints path.

How do you generate fake user agents for test data and crawler testing?

Generate fictional UA strings by emitting values that match real browser patterns (correct token order, plausible version numbers) and attaching them to test fixtures or synthetic accounts. Use them to exercise device-detection branches, rate-limit rules, and crawler-handling logic. Keep every UA fictional and pair it with documentation-range data so nothing maps to a real device or person [rfc9110].

Practical uses for developers and QA:

  • Responsive and feature-branch testing: feed mobile, desktop, and tablet UAs through your detection layer to confirm each path renders correctly.
  • Crawler-handling tests: simulate a bot UA to verify your robots logic, rate limits, and server-rendered fallbacks behave, without hammering anyone else's site.
  • Analytics validation: send a known mix of UAs and assert your pipeline classifies browser, OS, and device buckets the way you expect.
  • Client Hints coverage: pair each generated UA with matching `Sec-CH-UA-*` headers so both code paths are exercised in CI.

How do you keep generated user agents ethical and non-identifying?

Treat a generated UA like any synthetic identifier: it should describe a plausible-but-fictional client, not a real one. The standards bodies reserve specific ranges precisely so test data never collides with production. Mirror that discipline by combining fictional UAs with documentation IPs and reserved phone numbers, never real customer fingerprints.

Data typeSafe reserved rangeDefined by
IPv4 documentation192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24RFC 5737
IPv6 documentation2001:db8::/32RFC 3849
Example domainsexample.com, example.org, example.netRFC 2606
North American test phone555-0100 through 555-0199NANPA fictitious numbers
Private device IDsSynthetic UUIDs you mint locallyRFC 4122 (random v4)
Reserved and documentation ranges to pair with fictional UA strings in test fixtures.

Our generators follow exactly this approach. The fake person generator and the broader full identity toolset emit consistent, never-issued identifiers, so a generated UA travels alongside a fictional name, a documentation-range address, and a sandbox phone number. Nothing in the bundle resolves to a living person, which keeps the data usable for QA and safe for privacy review.

When you wire UA generation into automated tests, validate the strings at the boundary: confirm token order, reject malformed inputs, and log the generated value so a failing test is reproducible. A UA that does not parse in your own detector is a UA that will silently skew analytics in production.

References & sources

  1. User-Agent HTTP header referenceMDN Web Docs
  2. RFC 9110: HTTP Semantics, Section 10.1.5 User-AgentIETF
  3. History of the browser user-agent stringWebAIM
  4. Browser detection using the user agent (and why to avoid it)MDN Web Docs
  5. User-Agent ReductionChrome for Developers
  6. User-Agent Client Hints explainerWICG

Frequently asked questions

What is a user agent string?+

A User-Agent string is a value in the HTTP User-Agent request header that identifies the application, operating system, vendor, and version of the software making a request. Browsers send it so servers can adapt responses, but it is freely editable and should be treated as a hint, not proof of identity.

Why does every browser start with Mozilla/5.0?+

The Mozilla/5.0 prefix is a legacy compatibility token. In the 1990s, servers gated frame support on the Mozilla token, so competing browsers copied it. Every modern browser still leads with Mozilla/5.0 to avoid breaking ancient server-side sniffing logic that expects it.

Is faking a user agent illegal?+

Setting a custom User-Agent is normal and legal for testing, debugging, and crawler development; browsers and tools expose it deliberately. What crosses legal and ethical lines is using a forged UA to commit fraud, bypass access controls, or impersonate a real person or organization in violation of a site's terms.

What is User-Agent reduction in Chrome?+

User-Agent reduction is Google's program to shrink the Chrome UA string to a fixed, low-entropy form. Minor version, patch numbers, and detailed OS info are frozen or removed, and sites that need that data request it explicitly through User-Agent Client Hints instead.

What are User-Agent Client Hints?+

User-Agent Client Hints (UA-CH) are a set of HTTP request headers and a JavaScript API that expose browser and platform details on demand. Instead of cramming everything into one UA string, servers ask for specific hints like Sec-CH-UA-Platform or Sec-CH-UA-Mobile only when needed.

How do I generate a fake user agent for testing?+

Use a generator that emits syntactically valid UA strings matching real browser patterns, then assign them to test fixtures or synthetic accounts. Pair them with documentation-range data so no value maps to a real device or person. Our generators at / and /fake-person-generator produce consistent fictional profiles for this.

We use cookies for analytics and ads to keep this generator free. See our Privacy Policy.