- Home
- HTTP Headers
- User-Agent Header
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.
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
- Open DevTools → Network tab
- Look at any request
- Find User-Agent in request headers
- 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'
}
Related Headers
- Accept - Content type preferences
- Accept-Language - Language preferences
- Referer - Previous page information
- Origin - Request origin for CORS
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.