X Xerobit

User Agent Detection — Parse Browser and Device from User-Agent String

User agent strings identify the browser, OS, and device making an HTTP request. Here's how to parse user agents reliably, detect mobile vs desktop, and use structured data...

Mian Ali Khalid · · 5 min read
Use the tool
User Agent Parser
Parse any User-Agent string into browser, OS, device, and engine. Or detect your own. Built on the maintained ua-parser-js dataset.
Open User Agent Parser →

User agent detection extracts structured browser, OS, and device information from the User-Agent HTTP header. The raw string is human-readable but inconsistently formatted — libraries parse it into reliable structured data. Here’s how to parse user agents correctly and avoid the pitfalls of manual string matching.

Use the User Agent Parser to parse any user agent string online.

What the User-Agent header contains

A modern browser user agent string:

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

Breaking it down:

  • Mozilla/5.0 — legacy token, present in almost every UA for compatibility
  • (Windows NT 10.0; Win64; x64) — OS information
  • AppleWebKit/537.36 — rendering engine
  • (KHTML, like Gecko) — WebKit compatibility token
  • Chrome/122.0.0.0 — browser name and version
  • Safari/537.36 — WebKit compatibility token

The format evolved historically and is full of legacy tokens — “Mozilla” in Chrome’s UA is meaningless, as is “Safari” at the end.

User agent examples by platform

# Chrome on Windows:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36

# Firefox on macOS:
Mozilla/5.0 (Macintosh; Intel Mac OS X 14.3; rv:123.0) Gecko/20100101 Firefox/123.0

# Safari on iPhone:
Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Mobile/15E148 Safari/604.1

# Chrome on Android:
Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.105 Mobile Safari/537.36

# Googlebot:
Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)

# curl:
curl/8.4.0

# Python requests:
python-requests/2.31.0

JavaScript (ua-parser-js)

npm install ua-parser-js
import UAParser from 'ua-parser-js';

const ua = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Mobile/15E148 Safari/604.1';

const parser = new UAParser(ua);
const result = parser.getResult();

console.log(result);
/*
{
  ua: "Mozilla/5.0 ...",
  browser: { name: "Mobile Safari", version: "17.3", major: "17" },
  engine: { name: "WebKit", version: "605.1.15" },
  os: { name: "iOS", version: "17.3" },
  device: { vendor: "Apple", model: "iPhone", type: "mobile" },
  cpu: { architecture: undefined }
}
*/

// Check device type:
const isMobile = result.device.type === 'mobile';
const isTablet = result.device.type === 'tablet';
const isDesktop = !result.device.type;

// Check specific browser:
const isChrome = result.browser.name === 'Chrome';
const isSafari = result.browser.name === 'Safari';
const isFirefox = result.browser.name === 'Firefox';

// Get browser version number:
const chromeVersion = parseInt(result.browser.major);

Node.js (Express middleware)

import express from 'express';
import UAParser from 'ua-parser-js';

const app = express();

// Middleware to parse user agent for all requests:
app.use((req, res, next) => {
  const ua = new UAParser(req.headers['user-agent']);
  req.userAgent = ua.getResult();
  next();
});

app.get('/api/data', (req, res) => {
  const { device, browser, os } = req.userAgent;
  
  // Log device type for analytics:
  console.log(`Request from: ${device.type || 'desktop'} | ${browser.name} ${browser.version} | ${os.name}`);
  
  // Return different response for mobile:
  if (device.type === 'mobile') {
    return res.json({ data: 'mobile optimized response' });
  }
  
  res.json({ data: 'full response' });
});

Python (user-agents library)

pip install user-agents
from user_agents import parse

ua_string = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Mobile/15E148 Safari/604.1'

ua = parse(ua_string)

print(ua.browser.family)   # "Mobile Safari"
print(ua.browser.version)  # (17, 3, 0)
print(ua.os.family)        # "iOS"
print(ua.os.version)       # (17, 3, 0)
print(ua.device.family)    # "iPhone"
print(ua.device.brand)     # "Apple"
print(ua.device.model)     # "iPhone"

# Boolean helpers:
print(ua.is_mobile)   # True
print(ua.is_tablet)   # False
print(ua.is_pc)       # False
print(ua.is_bot)      # False

# In Django view:
from django.http import HttpRequest

def my_view(request: HttpRequest):
    ua = parse(request.META.get('HTTP_USER_AGENT', ''))
    if ua.is_mobile:
        return mobile_response()
    return desktop_response()

Bot detection

Bots, crawlers, and automated tools have distinct user agents:

const botPatterns = [
  /bot/i,
  /crawl/i,
  /spider/i,
  /googlebot/i,
  /bingbot/i,
  /slurp/i,
  /duckduckbot/i,
  /facebookexternalhit/i,
  /curl/i,
  /wget/i,
  /python-requests/i,
  /axios/i,
];

function isBot(userAgent) {
  return botPatterns.some(pattern => pattern.test(userAgent));
}

// Better: use ua-parser-js (has bot database):
const result = new UAParser(userAgentString).getResult();
const isBot = result.browser.name === undefined;  // Bots often have no recognized browser

Client hints: the modern alternative

The traditional User-Agent header is being replaced by Client Hints (privacy-preserving, structured):

// Request additional client hints in HTTP response header:
// Accept-CH: Sec-CH-UA, Sec-CH-UA-Mobile, Sec-CH-UA-Platform

// In JavaScript, read client hints:
// await navigator.userAgentData.getHighEntropyValues(['platform', 'platformVersion', 'architecture'])

const uaData = navigator.userAgentData;
console.log(uaData.brands);   // [{ brand: 'Google Chrome', version: '122' }, ...]
console.log(uaData.mobile);   // true/false
console.log(uaData.platform); // "Windows"

// Server-side via headers:
// Sec-CH-UA: "Chromium";v="122", "Not(A:Brand";v="24"
// Sec-CH-UA-Mobile: ?0
// Sec-CH-UA-Platform: "Windows"

Client hints reduce the privacy concerns of full UA strings (which can be used for fingerprinting) while providing the structure that developers need.

What NOT to do: manual string matching

// FRAGILE: breaks with new browsers, versions, or UA string changes
function isMobile(ua) {
  return /Mobile|Android|iPhone|iPad/.test(ua);
}

// FRAGILE: version number position changes between browsers
function getChromeVersion(ua) {
  const match = ua.match(/Chrome\/(\d+)/);
  return match ? parseInt(match[1]) : null;
}

Manual string matching requires constant updates as new browsers and devices are released. Use a maintained library that has these patterns built in.


Related posts

Related tool

User Agent Parser

Parse any User-Agent string into browser, OS, device, and engine. Or detect your own. Built on the maintained ua-parser-js dataset.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.