HTTP

Header

User-Agent Header

Learn how the User-Agent header identifies the client software, browser, or application making HTTP requests. Understand user agent strings and best practices.

6 min read beginner Try in Playground

TL;DR: Identifies the client software (browser, app, bot) making the request. Use for analytics and content adaptation, but prefer feature detection over browser sniffing.

What is User-Agent?

The User-Agent header identifies the client software making the request. It’s like showing your ID card—it tells the server what browser, app, or tool you’re using. Servers use this information to serve appropriate content, track usage, and provide compatibility.

This header helps servers understand client capabilities and optimize responses accordingly.

How User-Agent Works

Browser sends identification:

GET /page HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
```text

**Server adapts response:**

```javascript
// Server detects mobile browser and serves mobile-optimized content
if (req.headers['user-agent'].includes('Mobile')) {
  res.render('mobile-page')
} else {
  res.render('desktop-page')
}

Common User-Agent Strings

Desktop Browsers

Chrome on Windows:

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
```text

**Firefox on macOS:**

```http
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/121.0

Safari on macOS:

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15
```text

**Edge on Windows:**

```http
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0

Mobile Browsers

Chrome on Android:

User-Agent: Mozilla/5.0 (Linux; Android 14; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36
```text

**Safari on iPhone:**

```http
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1

API Clients and Tools

curl:

User-Agent: curl/8.4.0
```text

**Postman:**

```http
User-Agent: PostmanRuntime/7.36.0

Custom API Client:

User-Agent: MyApp/1.2.3 (https://myapp.com)
```javascript

## Server-Side Detection

### Browser Detection

```javascript
function detectBrowser(userAgent) {
  const ua = userAgent.toLowerCase()

  if (ua.includes('chrome') && !ua.includes('edg')) return 'chrome'
  if (ua.includes('firefox')) return 'firefox'
  if (ua.includes('safari') && !ua.includes('chrome')) return 'safari'
  if (ua.includes('edg')) return 'edge'
  if (ua.includes('opera')) return 'opera'

  return 'unknown'
}

app.use((req, res, next) => {
  req.browser = detectBrowser(req.headers['user-agent'] || '')
  next()
})

Mobile Detection

function isMobile(userAgent) {
  const mobileKeywords = ['Mobile', 'Android', 'iPhone', 'iPad', 'Windows Phone', 'BlackBerry']

  return mobileKeywords.some((keyword) => userAgent.includes(keyword))
}

app.use((req, res, next) => {
  req.isMobile = isMobile(req.headers['user-agent'] || '')
  next()
})
```javascript

### Operating System Detection

```javascript
function detectOS(userAgent) {
  const ua = userAgent.toLowerCase()

  if (ua.includes('windows nt')) return 'windows'
  if (ua.includes('mac os x')) return 'macos'
  if (ua.includes('linux')) return 'linux'
  if (ua.includes('android')) return 'android'
  if (ua.includes('iphone') || ua.includes('ipad')) return 'ios'

  return 'unknown'
}

Content Adaptation

Responsive Content Serving

app.get('/dashboard', (req, res) => {
  const userAgent = req.headers['user-agent'] || ''

  if (isMobile(userAgent)) {
    res.render('dashboard-mobile', {
      simplified: true,
      touchOptimized: true
    })
  } else {
    res.render('dashboard-desktop', {
      fullFeatures: true,
      keyboardShortcuts: true
    })
  }
})
```javascript

### Feature Detection

```javascript
app.get('/app', (req, res) => {
  const userAgent = req.headers['user-agent'] || ''
  const browser = detectBrowser(userAgent)

  const features = {
    webgl: ['chrome', 'firefox', 'safari', 'edge'].includes(browser),
    webrtc: ['chrome', 'firefox', 'edge'].includes(browser),
    serviceWorker: ['chrome', 'firefox', 'edge'].includes(browser)
  }

  res.render('app', { features })
})

API Version Selection

app.use('/api', (req, res, next) => {
  const userAgent = req.headers['user-agent'] || ''

  // Serve different API versions based on client
  if (userAgent.includes('MyApp/1.0')) {
    req.apiVersion = 'v1'
  } else if (userAgent.includes('MyApp/2.0')) {
    req.apiVersion = 'v2'
  } else {
    req.apiVersion = 'latest'
  }

  next()
})
```javascript

## Security and Analytics

### Bot Detection

```javascript
function isBot(userAgent) {
  const botKeywords = [
    'bot',
    'crawler',
    'spider',
    'scraper',
    'Googlebot',
    'Bingbot',
    'facebookexternalhit'
  ]

  return botKeywords.some((keyword) => userAgent.toLowerCase().includes(keyword.toLowerCase()))
}

app.use((req, res, next) => {
  if (isBot(req.headers['user-agent'] || '')) {
    req.isBot = true
    // Maybe serve different content or rate limit
  }
  next()
})

Analytics Tracking

app.use((req, res, next) => {
  const userAgent = req.headers['user-agent'] || ''

  analytics.track('page_view', {
    path: req.path,
    browser: detectBrowser(userAgent),
    os: detectOS(userAgent),
    mobile: isMobile(userAgent),
    bot: isBot(userAgent)
  })

  next()
})
```javascript

### Security Filtering

```javascript
app.use((req, res, next) => {
  const userAgent = req.headers['user-agent'] || ''

  // Block suspicious or malicious user agents
  const blockedPatterns = [/sqlmap/i, /nikto/i, /nmap/i, /masscan/i]

  if (blockedPatterns.some((pattern) => pattern.test(userAgent))) {
    return res.status(403).json({ error: 'Blocked user agent' })
  }

  next()
})

Client-Side Customization

Custom User-Agent in JavaScript

// Note: Browsers don't allow changing User-Agent in fetch/XMLHttpRequest
// But you can add custom headers for identification

fetch('/api/data', {
  headers: {
    'X-Client-Version': 'MyApp/2.1.0',
    'X-Client-Platform': 'web'
  }
})
```javascript

### API Client User-Agent

```javascript
// Node.js API client
const axios = require('axios')

const client = axios.create({
  headers: {
    'User-Agent': 'MyAPIClient/1.0.0 (https://myapp.com; support@myapp.com)'
  }
})

Mobile App User-Agent

// React Native or mobile app
const customUserAgent = `MyApp/1.2.3 (${Platform.OS} ${Platform.Version}; ${Device.modelName})`

fetch('/api/data', {
  headers: {
    'User-Agent': customUserAgent
  }
})
```text

## Best Practices

### Don't Rely Solely on User-Agent

```javascript
// ❌ Fragile: User-Agent can be spoofed or change
if (userAgent.includes('Chrome/120')) {
  enableNewFeature()
}

// ✅ Better: Feature detection
if (typeof window.fetch !== 'undefined') {
  enableNewFeature()
}

Graceful Degradation

app.get('/app', (req, res) => {
  const userAgent = req.headers['user-agent'] || ''
  const capabilities = detectCapabilities(userAgent)

  // Provide fallbacks for unsupported features
  res.render('app', {
    useModernJS: capabilities.es6,
    useWebGL: capabilities.webgl,
    fallbackMode: !capabilities.es6
  })
})
```text

### Respectful Bot Handling

```javascript
app.use((req, res, next) => {
  if (isBot(req.headers['user-agent'] || '')) {
    // Serve bot-friendly content
    res.set('Cache-Control', 'public, max-age=3600')
    req.botOptimized = true
  }
  next()
})

Privacy Considerations

// Don't log full user agents (privacy)
function sanitizeUserAgent(userAgent) {
  // Keep browser/OS info, remove detailed version numbers
  return userAgent.replace(/\d+\.\d+\.\d+/g, 'x.x.x')
}

analytics.track('page_view', {
  userAgent: sanitizeUserAgent(req.headers['user-agent'] || '')
})
```text

## Testing User-Agent

### Using curl

```bash
# Default curl user agent
curl https://example.com/

# Custom user agent
curl -H "User-Agent: MyBot/1.0" https://example.com/

# Mimic browser
curl -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" \
     https://example.com/

Browser DevTools

  1. Open DevTools → Network tab
  2. Look at any request
  3. Find User-Agent in request headers
  4. Use DevTools to simulate different devices

JavaScript Detection

// Client-side user agent
console.log('User Agent:', navigator.userAgent)

// Server-side (Node.js)
console.log('User Agent:', req.headers['user-agent'])
```javascript

## Common Patterns

### Progressive Enhancement

```javascript
const capabilities = {
  basic: true,
  enhanced: userAgent.includes('Chrome') || userAgent.includes('Firefox'),
  premium: userAgent.includes('Chrome/1') && !isMobile(userAgent)
}

res.render('page', { capabilities })

A/B Testing

function getUserSegment(userAgent) {
  const hash = crypto.createHash('md5').update(userAgent).digest('hex')
  return parseInt(hash.substring(0, 2), 16) % 2 === 0 ? 'A' : 'B'
}

User Agent Sniffing vs Feature Detection

The fundamental problem with using User-Agent for capability detection is that the string is unreliable. Browsers have historically added tokens to appear compatible with servers that check for specific browsers, creating strings like Mozilla/5.0 that appear in every browser regardless of the actual rendering engine. The string is also easily spoofed by any HTTP client.

Feature detection is the correct alternative for client-side capability checks. Instead of checking if the browser is Chrome, check if window.IntersectionObserver exists. Instead of checking for Safari, check if CSS.supports('display', 'grid') returns true. This approach works correctly for new browser versions, obscure browsers, and future browsers that did not exist when you wrote the code.

For server-side use cases like analytics, bot detection, and content adaptation, User-Agent remains useful despite its limitations. The key is to treat it as a hint rather than a guarantee. Bot detection in particular benefits from combining User-Agent analysis with behavioral signals like request rate, IP reputation, and JavaScript execution capability, since sophisticated bots can spoof any User-Agent string.

Frequently Asked Questions

What is the User-Agent header?

User-Agent identifies the client software making the request. It typically includes browser name, version, operating system, and device information.

Should I use User-Agent for feature detection?

No, use feature detection instead. User-Agent strings are unreliable, often spoofed, and browser sniffing leads to maintenance problems. Test for features directly.

Why are User-Agent strings so long?

Historical compatibility. Browsers add tokens like Mozilla/5.0 to appear compatible with older servers. The format evolved chaotically over decades.

What is User-Agent Client Hints?

Client Hints is a modern replacement providing structured data via separate headers like Sec-CH-UA. It is more reliable and privacy-preserving than parsing User-Agent.

Keep Learning