Chrome's User-Agent today:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36It starts with Mozilla, includes AppleWebKit, name-drops Chrome, and ends with Safari. Why does Chrome carry the names of Netscape, Apple, KHTML, Gecko, and Safari all at once? This guide walks through the User-Agent's 30-year history of impersonation, how to parse it, and what's coming next with Sec-CH-UA.
1993 — when it was simple
NCSA Mosaic's User-Agent:
NCSA_Mosaic/2.0 (Windows 3.1)Browser name + version + OS. RFC 1945 (HTTP/1.0) format. The server learns the client and adapts — a reasonable design.
1994 — Netscape arrives and "Mozilla" is born
Netscape Navigator launched in 1994. Codename: Mozilla (Mosaic Killer). User-Agent:
Mozilla/1.0 (Win3.1)Netscape was first to ship frames and JavaScript. Some sites began checking User-Agent: "if Mozilla, serve the modern features; otherwise plain HTML."
1995 — IE arrives, impersonation begins
Microsoft shipped IE 1.0. Problem: sites that only "supported Mozilla" served IE the dumbed-down HTML. Microsoft's solution:
Mozilla/1.22 (compatible; MSIE 2.0; Windows 95)
^^^^^^^ ^^^^^^^^^
the first lie the real identityIE pretended to be Mozilla, hiding "MSIE 2.0" inside the compatible; parenthetical. Site branch logic saw "Mozilla" and let IE through.
Trust in User-Agent broke here. Every new browser since has used the same trick.
1998-2003 — Mozilla / Gecko rebirth
Netscape opened its source; the Mozilla Foundation was born. The new rendering engine was called Gecko. Firefox (then Phoenix → Firebird → Firefox) had:
Mozilla/5.0 (Windows NT 5.1; rv:1.7) Gecko/20040616 Firefox/0.9.12003 — Safari and the KHTML masquerade
Apple shipped Safari. Its rendering engine was a fork of KDE's KHTML. Since some sites only supported KHTML, Apple impersonated it too:
Mozilla/5.0 (Macintosh; ...) AppleWebKit/85.7 (KHTML, like Gecko) Safari/85.7
^^^^^^^ ^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^
Netscape Apple engine "like KHTML + real
lie (a KHTML fork) Gecko" identityFour layers of disguise:
- Mozilla → Netscape compatible (legacy from 1995)
- AppleWebKit → Apple's engine (originally a KHTML fork)
- KHTML, like Gecko → "pass both KHTML and Gecko sniffing branches"
- Safari → real identity
2008 — Chrome takes the impersonation to its peak
Google released Chrome. Engine = WebKit (Apple's, itself a KHTML fork). User-Agent:
Mozilla/5.0 (Windows NT 6.0) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13
^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^
Netscape Apple "KHTML / Gecko" Chrome Safari
(real) (Safari-sniffing fallback)Five layers. Every lie inherited. This is the format every browser still uses today.
2013 — Edge joins
Microsoft retired IE and launched Edge. First with EdgeHTML:
Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0 Safari/537.36 Edge/15.0In 2020 Edge moved to Chromium. Now it's nearly identical to Chrome:
...AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0The only Edge-specific hint is Edg/ (yes, "Edg" not "Edge") at the end.
Mobile adds extra chaos
iOS Safari:
Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1Android Chrome:
Mozilla/5.0 (Linux; Android 14; SM-S918N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36Mobile OS + device model (SM-S918N = Galaxy S23 Ultra) + the Mobile keyword. Longer and busier.
Parsing User-Agent
Parsing UA with regex is a parade of edge cases. Standard libraries:
- ua-parser-js — 5,000+ regex patterns. User-Agent Parser uses this library
- useragent (Node) / woothee (Go) — similar pattern databases
Output is usually four buckets: browser name / version / OS / device.
New devices and browsers require frequent DB updates — UA parser libraries publish often.
Why everyone lied — the tragedy of sniffing
Sites branched on User-Agent like this:
if (userAgent.includes("Mozilla")) {
// modern features (JavaScript, frames, etc.)
} else {
// basic HTML
}Whenever a new browser appeared:
- Old sites didn't recognize it → basic HTML
- New browser disguised itself with an older name → got the modern features
- The disguise became standard → next new browser inherited it
Thirty years of accumulated disguise. Every browser pretends to be Mozilla / Apple / Safari.
Feature detection is the modern answer
Best practice — don't sniff UA, detect features directly:
// Bad — UA sniffing
if (navigator.userAgent.includes("Chrome")) { ... }
// Good — feature detection
if ("IntersectionObserver" in window) { ... }
if (CSS.supports("display", "grid")) { ... }Reasonable exceptions — browser-specific bug workarounds, mobile UI tuning, analytics. Even there, more precise signals than UA usually exist (Touch API, viewport size, hover support).
Sec-CH-UA — the Client Hints future
Google proposed this in 2020. It tries to fix UA's privacy and accuracy problems:
- The default UA is generic — version detail is hidden (privacy)
- Servers opt into more detail via HTTP Client Hints
// Client → server (initial request)
Sec-CH-UA: "Chromium";v="120", "Google Chrome";v="120", "Not_A.Brand";v="8"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"
// If the server needs more
Accept-CH: Sec-CH-UA-Full-Version-List, Sec-CH-UA-Model"Not_A.Brand" is the GREASE pattern — a fake brand for forward compatibility, deliberately so servers can't hardcode brand lists.
Privacy budget — request too much detail and the browser refuses. Defense against fingerprinting.
Common pitfalls
1. UA spoofing
Clients change UA freely — browser dev tools, curl's -A flag, bots in disguise. Never base security decisions on UA alone — combine with IP, rate limits, auth.
2. iPad's UA chaos
Since iOS 13, the iPad's default UA is identical to desktop Safari:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...Apple's intentional choice — serve the desktop site. UA can't tell iPad from Mac. Use navigator.maxTouchPoints > 0 for touch detection.
3. Bot-UA over-blocking
Blocking any UA containing "bot" blocks Googlebot too. Bot gating needs IP ranges (verifiable Googlebot IPs) plus UA patterns.
4. UA string length
Mobile UAs hit 400+ characters. VARCHAR(256) log tables truncate. Use 512+ when you care about full UA preservation.
5. Relying solely on Sec-CH-UA
Client Hints are still rolling out — Firefox and Safari support is partial. Keep UA + Client Hints as fallback paths.
References
- History of the browser user-agent string — WebAIM
- Sec-CH-UA / User-Agent Client Hints — W3C draft
- ua-parser-js — GitHub
- RFC 7231 (HTTP/1.1 User-Agent header) — datatracker
Summary
- 1993 NCSA Mosaic was honest. 1995 IE started impersonating Mozilla. 30 years of compounding disguise since.
- Chrome's UA carries four impersonations (Mozilla, AppleWebKit, KHTML, Safari) before its real name.
- Sniffing forced every new browser to inherit older names just to slip through site branches.
- Modern best practice = feature detection, not UA sniffing.
- Sec-CH-UA (Client Hints) trades privacy + accuracy. Still rolling out across browsers.
- Clients can spoof UA freely — never the only signal for security decisions.
- Parsing — User-Agent Parser uses ua-parser-js to extract browser / engine / OS / device.