A user agent string is a line of text a browser or client sends in the User-Agent HTTP header to identify itself, describing the application, its version, the rendering engine, and the operating system to the server. Servers and scripts read it to understand what is making a request, from a desktop browser to a mobile app or a search engine crawler. It is useful for analytics and troubleshooting, but it is also client-controlled, historically messy, and increasingly restricted for privacy, so it must be used with care.
Key takeaways
- The user agent string identifies the client in the
User-Agentrequest header. - It is readable server-side and in JavaScript via
navigator.userAgent. - Its format is a loose, historically-driven list of tokens, not a strict schema.
- It is trivially spoofable, so never trust it for security decisions.
- Prefer feature detection over user agent sniffing for browser capabilities.
- Client Hints and UA reduction are the modern, privacy-focused direction.
What does a user agent string look like?
A typical desktop browser string is a chain of tokens accumulated over decades of web history. For example:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Reading it loosely: Mozilla/5.0 is a legacy compatibility token present in nearly every browser, the parentheses hold platform details, AppleWebKit and the like Gecko note describe the engine lineage, and the Chrome and Safari tokens indicate the browser. The apparent contradictions are deliberate: browsers historically imitated each other to satisfy servers that sniffed for specific names.
This accumulation of legacy tokens is the single biggest reason the user agent is so awkward to work with. Each token was added to keep some old server happy, and none could safely be removed without breaking sites that still checked for it. The result is a string that reads less like a clean identifier and more like a fossil record of the browser wars, which is precisely why parsing it by hand is a losing battle.
How do you read the user agent string?
You can access it on both sides of a request.
On the server, it arrives as a request header:
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) ...
In the browser, JavaScript exposes it directly:
const ua = navigator.userAgent;
console.log(ua);
Because the format has no guaranteed structure, extracting reliable fields with your own regular expressions is fragile. Teams that must parse it usually rely on a maintained parsing library and treat the results as hints rather than facts.
Why is the user agent string so unreliable?
Several properties make the user agent a weak foundation for decisions:
- It is client-controlled. Any client can set any value, so it is easily spoofed, and privacy tools and developer settings change it routinely.
- It has no strict grammar. Tokens vary by vendor and version, so parsing is guesswork that breaks over time.
- It is intentionally misleading. The historical
Mozillatoken and cross-imitation mean the string does not literally state what it appears to. - It is being deliberately reduced. Browsers are trimming detail to fight fingerprinting, so precise version and platform data may no longer be present.
When should you use the user agent, and when not?
The distinction between acceptable and risky uses is important.
| Use case | Advisable? | Better approach |
|---|---|---|
| Analytics and traffic breakdowns | Acceptable | Parse as approximate, expect noise |
| Detecting bots for logging | Acceptable | Verify important bots by reverse DNS |
| Choosing which features to enable | Avoid | Feature detection in code |
| Access control or licensing | Never | Real authentication |
| Serving different content to crawlers | Never | Consistent content (cloaking risks penalties) |
Why is feature detection better than user agent sniffing?
User agent sniffing infers capabilities from the browser name and version, which breaks whenever strings change or a new browser appears. Feature detection instead asks directly whether the capability you need exists.
// Fragile: sniffing the user agent
if (navigator.userAgent.includes("Chrome")) { /* assume support */ }
// Robust: feature detection
if ("IntersectionObserver" in window) {
// use the API
} else {
// load a fallback
}
Feature detection is future-proof because it tests reality rather than a self-reported label, and it works even when a browser spoofs its identity.
What are Client Hints and UA reduction?
To replace fragile parsing and reduce passive fingerprinting, browsers are moving toward User-Agent Client Hints. Instead of packing everything into one opaque string, the server requests only the specific pieces it needs, and the browser supplies them as structured headers or via a JavaScript API.
- Low-entropy hints, such as a coarse platform and whether the device is mobile, are available by default.
- High-entropy hints, such as the full platform version or CPU architecture, are provided only when the site explicitly asks, giving users more privacy.
- The traditional user agent string is being frozen and trimmed over time, so long-term code should prefer Client Hints where precise data is required.
How do you parse a user agent safely in code?
Sometimes you genuinely need to categorize traffic, for example to break down analytics by device type or to log which browsers hit an endpoint. When you do, treat the user agent as approximate data and follow a few rules that keep the results useful and safe.
- Use a maintained library, not homegrown regex. The format is too irregular and changes too often for hand-written patterns to stay correct. A community-maintained parser is updated as new strings appear.
- Treat every field as a hint. Expect unknown, empty, or contradictory values, and always have a sensible default bucket for anything you cannot classify.
- Never branch on it for correctness or security. Analytics and logging are fine; deciding whether to grant access, serve a feature, or trust a request is not.
- Cap and sanitize the value before logging. A user agent is attacker-controlled input, so truncate it and avoid interpolating it into shells, SQL, or log formats that could be abused for injection.
Server-side, the header is read like any other request header:
const ua = req.headers["user-agent"] || "";
const isMobileHint = /Mobi|Android|iPhone/.test(ua); // approximate only
Even this simple mobile check is a heuristic that can misclassify tablets, embedded browsers, or spoofed clients, so use it for coarse reporting rather than anything a user would notice if it were wrong. For decisions that must be reliable, prefer Client Hints or feature detection instead.
How do you handle bots and crawlers correctly?
Bots identify themselves in the user agent, which is useful for logs and analytics, but because the string is spoofable, malicious traffic can impersonate a legitimate crawler. To trust a claimed identity for anything that matters:
- Check the user agent for the crawler name as a first filter.
- Perform a reverse DNS lookup on the requesting IP address.
- Confirm the hostname belongs to the crawler’s official domain, then optionally do a forward lookup back to the IP.
Also avoid serving different content to crawlers than to users based on the user agent, since that cloaking pattern can trigger search penalties. Verification, not blind trust, is the rule for anything security- or access-related.
Frequently asked questions
Where is the user agent string sent?
The browser sends it in the User-Agent HTTP request header on every request, and it is also readable in JavaScript through navigator.userAgent.
Why do all browsers mention Mozilla in their user agent?
It is a historical legacy. Early browsers copied the Mozilla token for compatibility with servers that checked for it, and the convention stuck across virtually every modern browser.
Can a user agent string be faked?
Yes, easily. It is just a header the client controls, so it can be changed or spoofed. Never rely on it alone for security, licensing, or access decisions.
Should I use the user agent for feature detection?
No. Prefer feature detection that tests whether a capability exists directly. User agent sniffing is brittle and breaks as strings change over time.
What are Client Hints?
Client Hints are a set of request headers and a JavaScript API that expose specific device and browser details on demand, offering a more structured, privacy-aware alternative to parsing the user agent.
How do I identify search engine bots reliably?
Check the user agent for the bot name, then verify by doing a reverse DNS lookup on the request IP and confirming it resolves to the official crawler domain, since the string alone is spoofable.
Why is the user agent string being reduced?
Browsers are freezing and trimming user agent detail to curb passive fingerprinting and privacy tracking, moving precise data behind Client Hints that sites request explicitly.