ua-parser-js patternsClient Hints comparisonUnmatched fields shown honestly

User Agent Parser

Parse a User-Agent string into browser, engine, operating system and device fields, or inspect the one your own browser is sending. All processing happens locally in your browser.

User agent strings

Parse a User-Agent string into browser, OS and device fields

Parsing uses the ua-parser-js library against its pattern database. A field that cannot be matched is reported as not stated rather than guessed, because a UA string genuinely does not always contain the answer.

Browser
Engine
Operating system
Device type
InputUser-Agent string
ResultParsed fields

Paste a User-Agent string to parse it.

Nothing parsed yet. Paste a string or use your own browser’s.

Load sampleLoad a string

What is a User-Agent string?

Every HTTP request carries a User-Agent header: a line of text the client uses to describe itself. Here is a modern one, taken apart:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
└────┬────┘ └──────────┬──────────┘ └──────┬──────┘ └─────────┬────────┘ └───────┬──────┘ └──────┬─────┘
  legacy         platform info         engine token      compatibility     actual browser   legacy token

Almost none of that is literally true. The browser is Chrome, not Mozilla. It does not use AppleWebKit; it uses Blink. It is not Safari. Every one of those tokens exists because at some point a website checked for it before deciding to serve modern content.

That history is why UA parsing is pattern matching against a curated database rather than reading a structured field. This page uses the ua-parser-js library, which maintains that database.

How to parse a string

  1. Paste a string, or use your own The "Use my browser" button inserts what your browser is currently sending, which is handy for comparing against a value from a log.
  2. Read the field table Browser, engine, OS, device and CPU are shown separately. A field the parser could not match reads "Not stated" instead of being filled with a guess.
  3. Check the match count It shows how many of the five field groups matched a known pattern. Zero is a real answer, not an error — see below.
  4. Copy the JSON if you need it The raw parser result is available as JSON for pasting into a ticket or a test fixture.

Why parsing is inherently imperfect

This is the most useful thing to understand about UA strings, and it is not a limitation of any particular parser.

Windows 11 is indistinguishable from Windows 10

Both report Windows NT 10.0. Microsoft never incremented the NT version in the UA string, so no parser can tell them apart. A tool that confidently says "Windows 11" from a UA string alone is guessing.

macOS versions froze at 10.15.7

Safari and Chrome both cap the reported macOS version at 10_15_7 regardless of the real version, deliberately, to reduce fingerprinting. A Mac running macOS 15 still reports Mac OS X 10_15_7.

iPads claim to be Macs

Since iPadOS 13, Safari on iPad sends a desktop macOS string by default. Device detection therefore reports a Mac, and there is no reliable way to distinguish them from the UA alone.

Chrome freezes its version and platform detail

Chromium's User-Agent Reduction programme deliberately caps the minor version at 0.0.0 and reduces platform detail. That is why every recent Chrome string ends 131.0.0.0 rather than a real build number.

Anyone can send anything

The header is a self-report with no verification. Browser devtools let you change it in two clicks, and every HTTP client sets whatever you tell it to. A UA string is a claim, not evidence.

Practical consequence: do not branch application behaviour on parsed UA fields. Use feature detection (if ('share' in navigator)), CSS media queries for layout, and progressive enhancement. UA parsing is for analytics, log triage and support conversations — describing traffic after the fact, not deciding what code to run.

Client Hints: the structured replacement

Chromium browsers expose navigator.userAgentData, which returns actual structured values instead of a string to be pattern-matched:

navigator.userAgentData
// {
//   brands: [
//     { brand: "Chromium",       version: "131" },
//     { brand: "Google Chrome",  version: "131" },
//     { brand: "Not_A Brand",    version: "24"  }
//   ],
//   mobile: false,
//   platform: "Windows"
// }

Two things are worth noting. The deliberately absurd "Not_A Brand" entry exists to break code that assumes a fixed position in the list — a lesson learned from the UA string era. And the fields shown by default are limited; details such as the full platform version require an explicit getHighEntropyValues() call, which is a separate, auditable request.

Firefox and Safari have not implemented Client Hints, so the UA string remains the only option there. When your browser supports it, this page shows both side by side so you can compare the structured answer with what the string claims.

Examples: what different clients produce

Client Browser Engine Device type
Chrome on Windows Chrome 131 Blink Not stated (desktop is implied by absence)
Safari on iPhone Mobile Safari 17.5 WebKit mobile, model iPhone, vendor Apple
Firefox on Android tablet Firefox 129 Gecko tablet
Edge on macOS Edge 131 Blink Not stated
Googlebot Not stated Not stated Not stated
curl/8.4.0 Not stated Not stated Not stated

Two details in that table are worth dwelling on.

Desktop has no device type. The device.type field is only populated for mobile, tablet, console, smarttv, wearable and embedded. An empty device type usually means desktop, but it can also mean unrecognised — so it is reported as "Not stated" rather than asserted as desktop.

Bots return nothing. The base library's pattern database targets browsers, so Googlebot and curl match none of it. That is genuinely informative: when you are triaging server logs and every field comes back empty, the request almost certainly came from an automated client rather than a browser. Distinguishing which bot needs a separate crawler database, which this page does not include and does not claim to.

Use cases

  • Reproducing a reported bug. A user pastes their UA into a support ticket; parsing it tells you the browser and engine to test against.
  • Triaging server log entries. Working out whether a burst of requests came from real browsers or from scripts.
  • Checking an analytics discrepancy. Comparing a raw UA string against how your analytics classified it explains an unexpected browser split.
  • Verifying a spoofed UA in testing. Confirming that the string your automated test sets is being read the way you expect.
  • Understanding what your own browser reveals. Seeing the string alongside Client Hints shows how much detail is actually exposed.
  • Auditing a UA allowlist. If a firewall or CDN rule matches on UA substrings, parsing sample strings shows which real clients it would catch or miss.

Working through a request end to end? The URL Parser breaks down the target URL and the cURL to Fetch Converter turns a captured request into code.

Privacy

Parsing runs in your browser using the bundled pattern database. The string you paste is not uploaded. Reading navigator.userAgent and navigator.userAgentData happens locally, and no high-entropy Client Hint values are requested.

Usage analytics record only the parsed browser name, OS name and device type — coarse categories such as "Chrome" and "Windows" — never the raw string you pasted.

Frequently Asked Questions

Why does my Windows 11 machine show as Windows 10?

Because the UA string reports "Windows NT 10.0" for both, and Microsoft never incremented it. No parser can distinguish them from the string alone. Chromium browsers can, via the platformVersion high-entropy Client Hint, but that requires an explicit request rather than reading the header.

Why does every field come back empty for Googlebot?

The pattern database targets browsers, and crawlers do not follow browser UA conventions. It is a useful signal rather than a failure: an all-empty result in log triage strongly suggests an automated client. Identifying a specific bot needs a dedicated crawler database, which this tool does not include.

Should I use UA parsing to decide which code path to run?

No. The header is self-reported, trivially spoofed, and increasingly reduced by browsers on purpose. Use feature detection for capabilities and media queries for layout. UA parsing is for describing traffic after the fact, not for branching behaviour.

Why does Chrome report version 131.0.0.0 with zeros?

Chromium's User-Agent Reduction freezes the minor, build and patch numbers at 0 to limit fingerprinting. The major version is still accurate. If you need the full build number, that is available only through the getHighEntropyValues() Client Hints call.

Why is device type empty for my desktop?

The field is only set for mobile, tablet, console, smarttv, wearable and embedded clients. Desktop is inferred from the absence of a type rather than stated. Because absence can also mean "unrecognised", this page shows "Not stated" instead of asserting desktop.

Can an iPad be distinguished from a Mac?

Not reliably from the UA string. Since iPadOS 13, Safari on iPad requests desktop sites by default and sends a macOS string. Touch-capability checks combined with screen dimensions are the usual workaround, and even those are heuristics.

What is the difference between the browser and the engine?

The engine renders pages; the browser is the application around it. Chrome, Edge, Opera, Brave and Vivaldi all use Blink, so a rendering bug in one usually appears in all of them. Safari uses WebKit and Firefox uses Gecko. When reproducing a layout bug, the engine is the field that matters.