User Agent Parser

Paste a User-Agent string and see the browser, rendering engine, operating system, device type and versions it claims. Pre-filled with your own. Parsing is heuristic by nature — this page says plainly where the guesswork is, and what modern browsers offer instead.

Samples:

Nothing you paste leaves your browser. Parsing runs locally in this page.

Paste a user agent to parse it.

The breakdown will appear here.

Client Hints reported by this browser

Checking…

What a user agent string is

Every HTTP request a browser makes carries a User-Agent header: a single line of free text in which the client announces what it claims to be. A modern desktop Chrome sends something close to Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, and almost none of that is true. It is not Mozilla, it is not AppleWebKit in the sense the token implies, it is not KHTML, and it is not Safari. The only informative token is Chrome/120.0.0.0, and even its last three components are deliberately zeroed.

The reason is thirty years of defensive imitation. In the mid-1990s some servers sent frame-capable markup only to clients identifying as Mozilla, so Internet Explorer began calling itself Mozilla/4.0 (compatible; MSIE 5.0; ...). Every subsequent browser inherited the habit, because a browser that told the truth was served a worse page than one that lied. Safari added WebKit while claiming KHTML; Chrome added its own token while claiming both WebKit and Safari; Edge, once it moved to Chromium, claims Chrome as well and distinguishes itself only by a trailing Edg/. The string grew by accretion and nothing was ever removed, because removing a token was the one change guaranteed to break someone's sniffing code.

The practical shape of that history is the parsing rule everything below depends on: order matters, and specificity wins. You must check for Edge before Chrome, for Chrome before Safari, for Opera's OPR/ before Chrome, and for the various iOS browsers — which are all obliged to use the system WebKit and so all look like Safari — before Safari itself. A parser that tests in the wrong order reports Chrome for every Edge user and Safari for everything on an iPhone.

How to read the parts

TokenWhat it usually means
Mozilla/5.0Nothing. A compatibility prefix carried by essentially every browser.
(Windows NT 10.0; Win64; x64)The platform comment. OS family, kernel version and CPU architecture. NT 10.0 covers both Windows 10 and 11 — they are indistinguishable here.
(Macintosh; Intel Mac OS X 10_15_7)macOS. The version has been frozen at 10.15.7 for years regardless of the real one.
(iPhone; CPU iPhone OS 17_1 like Mac OS X)iOS, with underscores instead of dots. An iPad reports CPU OS rather than iPhone OS — and iPadOS in desktop mode reports Macintosh instead.
(Linux; Android 13; Pixel 7)Android, with the OS version and a device model that newer Chrome releases replace with a generic K.
AppleWebKit/537.36Frozen. Present on Chrome, Edge, Opera and Safari alike and no longer distinguishes anything.
(KHTML, like Gecko)Pure archaeology. KHTML was the KDE engine WebKit forked from in 2001.
Chrome/120.0.0.0Chromium version. The last three components are zeroed as an anti-fingerprinting measure.
Edg/120.0.2210.91Edge. Deliberately not Edge/, which was the old EdgeHTML browser.
Version/17.1 Safari/604.1Safari's real version is in Version/; the Safari/ number is a WebKit build.
Firefox/121.0 with rv:121.0Gecko. The rv: value in the platform comment matches, except on Firefox for iOS, which sends FxiOS/ and runs WebKit.
Mobile / MobiThe most reliable phone signal available. An Android tablet is simply an Android UA without it.

Non-browser clients are the pleasant exception: they are short, honest and easy to parse. curl/8.4.0, python-requests/2.31.0, PostmanRuntime/7.36.0, Go-http-client/2.0, okhttp/4.12.0 and axios/1.6.2 each say exactly what they are. If you are working with these, the curl converter shows which User-Agent a copied command is carrying, and the API Tester lets you set one explicitly and see how a server's response changes.

Why this is unreliable, stated plainly

Every user agent parser, this one included, is a stack of ordered regular expressions maintained against a moving target. There is no grammar that identifies a browser unambiguously, no registry of legitimate values, and no verification of any kind. Three separate things make the result a guess rather than a fact.

The field is attacker-controlled and user-controlled. Anyone can send any string. Privacy extensions rotate or spoof it; developer tools change it with two clicks; scrapers copy a real Chrome string verbatim; bots pretending to be Googlebot are common enough that Google publishes reverse-DNS instructions for verifying the real one. Any decision with a security consequence that rests on this header is broken by construction — the same rule that applies to every other client-supplied HTTP header.

The information is deliberately being removed. Because a detailed UA is a strong fingerprinting signal, browsers now reduce it. Chrome and Edge freeze the minor version components at zero and generalise the platform version; Android device models are being replaced by a literal K; macOS has reported 10_15_7 for years no matter what version is actually running; Safari on iOS reports a coarse OS version. So the parser below can tell you the major browser version and very little else with confidence, and that is by design rather than by accident.

The heuristics decay. Every new browser that imitates an existing one, and every vendor that changes its format, silently breaks parsers written before it. Rules written for pre-Chromium Edge misidentify current Edge; rules written before OPR/ misidentify Opera; anything that assumes an iPad is not a Mac now misidentifies iPadOS in desktop mode. There is no version of this problem that stays solved.

What follows from this is a short list of things UA parsing is genuinely good for: aggregate analytics, where being wrong about a small percentage does not change the conclusion; reproducing a reported bug, where you want to know roughly what the reporter was running; picking the right app-store link on a download page; and identifying bots and API clients in server logs, where the clients are honest anyway. It is not good for deciding which code path to run.

Feature detection, and Client Hints

The correct replacement for asking which browser is this is asking does this browser do the thing I need. Feature detection — testing for the API, the CSS property or the format directly — remains correct when a browser gains the feature, when a browser you have never heard of already has it, and when a user spoofs their UA. Sniffing is wrong in all three cases. In practice that means checking 'IntersectionObserver' in window rather than a version number, CSS.supports() rather than a vendor name, and letting <picture> or content negotiation pick an image format rather than inferring it from a browser string.

Where you genuinely need client identity — analytics, or serving a platform-appropriate binary — the sanctioned mechanism is User-Agent Client Hints. Rather than one opaque line, the browser exposes structured values: a brand-and-version list, the platform, and a mobile boolean. Low-entropy hints arrive by default in the Sec-CH-UA, Sec-CH-UA-Mobile and Sec-CH-UA-Platform request headers, and are readable in JavaScript through navigator.userAgentData. Higher-entropy values — the full platform version, the device model, the full browser version list — must be requested explicitly, either by asking for them in JavaScript or by sending an Accept-CH response header, which makes the privacy cost of each one visible and auditable.

The honest caveat is that Client Hints are, at the time of writing, a Chromium feature; Firefox and Safari have not adopted them. So a real implementation reads navigator.userAgentData when it exists and falls back to parsing the string when it does not — which is why this page shows both. The panel above reports whatever your own browser exposes, so you can compare the structured answer with the parsed one directly.

One more note for anyone building a server that consumes this. The brand list in Client Hints deliberately includes a randomised nonsense brand — entries such as Not_A Brand with an arbitrary version — precisely to break code that assumes a fixed list or reads a fixed index. Iterate the list and match the brand you care about; never index into it. It is the same defensive instinct that produced the Mozilla/5.0 prefix in the first place, applied deliberately this time.

Working with user agents in API requests

User-Agent matters on the request side too, and more than people expect. A great many APIs require one and answer 403 Forbidden to requests without it — GitHub's API is the best-known example, and the failure is confusing because the endpoint works perfectly in a browser. Others rate-limit per user agent, or route requests differently based on it. When a script fails against an endpoint that works interactively, an absent or default User-Agent is worth ruling out early, alongside the usual 401 causes.

The convention for a client of your own is a descriptive product token with a contact point: something like my-service/1.4 (+https://example.com/bot). That gives an operator who sees unusual traffic in their logs a way to reach you instead of blocking you, and it is the difference between being treated as a partner and being treated as a scraper. Note also that in a browser User-Agent is a forbidden header name — fetch() will not let a page set it — so any test involving a custom UA has to come from a non-browser client. That is one of several things the CORS tester demonstrates directly, and one of the reasons our API Tester issues requests from a server rather than from the page.

If you are parsing user agents out of access logs at any volume, use a maintained library rather than hand-written patterns — the browser-vendor churn described above is exactly the maintenance burden a shared regex database exists to absorb. This tool is for the one-off case: a single string from a bug report, a log line you are trying to attribute, or a check on what your own browser is announcing. For the wider request-debugging loop, REST API testing covers where this fits.

Frequently asked questions

Why does every browser's user agent claim to be Mozilla?

It is thirty years of accumulated compatibility lying. Early servers sent frames only to Mozilla, so Internet Explorer called itself Mozilla/4.0 (compatible; MSIE ...). Every browser since has inherited that prefix, and each new engine added the tokens of its predecessors to avoid being served a degraded page: Chrome claims KHTML, like Gecko and Safari, Edge additionally claims Chrome. The result is a string whose first two thirds are historical noise and whose meaning lives entirely in the last few tokens.

Is user agent parsing reliable?

No, and it cannot be. The User-Agent header is a free-text field the client chooses; there is no grammar that identifies a browser unambiguously, no registry of values, and nothing preventing any client from sending any string. Every parser, including this one, is a pile of ordered heuristics that must be updated as vendors change their formats. Treat a parse as a guess that is usually right, never as a fact you can depend on.

Why are modern user agent strings frozen or reduced?

Because the string was a fingerprinting vector. A detailed UA carrying a precise browser build, an exact OS point release and a device model contributes a lot of entropy to identifying an individual browser across sites. Chrome, Edge and Safari now report reduced strings: minor version components are frozen at zero, and OS versions and device models are generalised or fixed. That is why a parser may report a version like 120.0.0.0, or an iPhone reporting an OS version that has not changed in years.

What are User-Agent Client Hints?

They are the sanctioned replacement for parsing the header. Instead of one opaque string, the browser exposes structured values — brand and version list, platform, mobile flag — through the Sec-CH-UA request headers and the navigator.userAgentData JavaScript API. Low-entropy hints are sent by default; high-entropy ones such as the full platform version or the device model must be requested explicitly, which makes the privacy cost visible. Support is currently Chromium-only, so a UA fallback is still needed.

Should I use UA sniffing in my application?

Almost never for deciding what code to run. Use feature detection: test for the capability you need rather than guessing from the browser name, because that stays correct when a browser gains the feature and when an unknown browser already has it. UA parsing is legitimate for analytics aggregates, for reproducing a reported bug, for routing app-store download links, and for identifying bots and API clients in server logs.

Is the user agent I paste sent to your servers?

No. Parsing happens entirely in this page with JavaScript, so nothing you paste is uploaded, logged or stored. That matters more than it sounds: a UA string pulled from a production access log can be surprisingly identifying on its own, since it may carry an app build number, a device model or an internal client name.

Related tools and reference