ScreenshotNeo

BlogGuides

What Is a User Agent? How Browsers Identify Themselves

A user agent is the software making an HTTP request. Learn what the User-Agent header reveals, why it can mislead, and when developers should use Client Hints.

By the ScreenshotNeo team4 October 20268 min read

A user agent is the client software that makes a request. A web browser is a common example, but command-line tools, crawlers, and other HTTP clients are user agents too. The User-Agent request header is a text description of that software; it can help a server understand a request, but it is not proof of which browser, device, or person sent it.

If you are deciding what a page should do, test the capability you need instead of guessing from the string. User-Agent values can include compatibility tokens, be changed by configuration, or expose less detail than older examples suggest. [RFC 9110] [MDN: Browser detection]

What does “user agent” mean?

In HTTP, the user agent is the software that originates a request. It is not the person using the software. A browser is a user agent when it requests a page; a script or API client is also a user agent when it makes an HTTP request. The phrase is broader than “browser.” [RFC 9110]

The term is also used for the User-Agent header field sent with an HTTP request. The header contains a text value describing the requesting software. RFC 9110 says a user agent SHOULD send this header on each request unless configured not to. “SHOULD” is a standards recommendation, not a guarantee that every request includes an unchanged value. [RFC 9110, Section 10.1.5]

What does a User-Agent string look like?

RFC 9110 describes the field as a product identifier followed by optional product identifiers and comments. A simplified shape is Product/Version (comment). Browser strings can contain several tokens, including compatibility labels retained for historical reasons. Do not read the whole string as a clean list of verified facts. [RFC 9110] [MDN: User-Agent header]

GET / HTTP/1.1
Host: example.com
User-Agent: ExampleBrowser/12.4 (Example OS; x86_64)

This is a schematic example, not a real browser string. A server can read the field like any other request header, but its contents are supplied by the client and may be reduced, customized, or misleading.

Why does a website inspect the User-Agent?

Servers may use the header to investigate interoperability problems, apply a workaround for a known client limitation, tailor a response, or analyze broad patterns in client usage. These are reasons to inspect it, not reasons to treat it as authoritative identity. [RFC 9110]

For example, a diagnostic log might record the header alongside the route, status code, and request time so an engineer can look for a pattern in reported failures. If you use it for analytics, remember that a value may not correspond neatly to a particular browser version or device.

Can a website identify your browser or device from it?

A User-Agent value can suggest the client software and some platform details. It cannot reliably prove them. Browser vendors may include compatibility tokens, users can configure or alter the field, and privacy features can reduce its detail. A matching token is evidence of what the request claims, not a trusted identity check. [RFC 9110] [MDN: User-Agent header]

It also does not identify the person using a browser. A detailed value can contribute to fingerprinting when combined with other characteristics, but the header alone should not be described as uniquely identifying every user. [RFC 9110]

Why User-Agent browser detection is brittle

Parsing the string to decide how a site behaves is called User-Agent sniffing. It is fragile because browser strings change, compatibility labels can confuse matching, and a browser name does not establish support for a particular feature. MDN recommends feature detection and progressive enhancement where possible. [MDN: Browser detection]

Test the capability you need

If the question is whether a JavaScript API exists, test for that API. If the question is whether a CSS feature works, use a CSS capability query or a suitable fallback. This directly answers the implementation question without relying on a browser label.

if ('IntersectionObserver' in window) {
  // Use the API when it is available.
} else {
  // Provide a fallback that still lets the page work.
}

Use browser-specific checks only for a narrow, documented workaround when there is no better capability test. Keep the workaround isolated and review it as browser behavior changes. Do not use the User-Agent string as an authentication or security boundary.

User-Agent reduction and privacy

Detailed browser, platform, and device information can add to fingerprinting risk. User-Agent reduction limits some passively exposed detail in browsers that support it. MDN describes examples that generalize platform or device information and reduce version detail; behavior differs by browser, so do not assume one universal reduced string. [MDN: User-Agent reduction]

When debugging, request or retain only the information needed for the job. Do not collect a detailed client profile merely because it is available.

User-Agent strings vs. Client Hints

Client Hints are an opt-in mechanism for asking a supporting browser for selected client information. A server can advertise requested hints with Accept-CH; a supporting client may then include corresponding Sec-CH-UA-* request headers. The JavaScript User-Agent Client Hints API exposes related information in supporting browsers. Higher-entropy values require an explicit API request. Support and behavior vary, and hints are not a substitute for feature detection when the real question is whether a capability works. [RFC 8942] [MDN: Client hints] [MDN: User-Agent Client Hints API]

Approach How information is shared Limit Best fit
User-Agent header Sent as a request header, commonly without a site first asking for particular details Can be verbose, reduced, configured, or misleading Interoperability diagnosis and broad client context
Client Hints The server requests selected information; a supporting client may provide it Support varies, and the client may not provide every requested value A specific, justified need for selected client details
Feature detection The page checks whether the needed behavior is available Answers capability questions, not general device identity Choosing implementation behavior

Request only hints that serve a specific purpose, account for browser support and user settings, and avoid treating any client-provided value as proof of identity. [RFC 8942] [MDN: Client hints]

Inspecting a User-Agent in code

For debugging, you can send a request with a chosen User-Agent value and inspect the response or log what your server receives. This shows how a client can set the header; it does not demonstrate a trustworthy way to identify browsers.

cURL

curl -i https://example.com \
  -H 'User-Agent: ExampleClient/1.0 (debug)'

Python

import requests

response = requests.get(
    "https://example.com",
    headers={"User-Agent": "ExampleClient/1.0 (debug)"},
    timeout=20,
)
print("Status:", response.status_code)
print("Server response:", response.headers.get("content-type"))

Node.js

const response = await fetch('https://example.com', {
  headers: { 'User-Agent': 'ExampleClient/1.0 (debug)' },
});
console.log('Status:', response.status);
console.log('Content-Type:', response.headers.get('content-type'));

In browser JavaScript, you can read the browser-provided value with navigator.userAgent, but it has the same limitations as the HTTP header. Do not assume it is a complete or truthful device description. [MDN: User-Agent header]

Common problems and fixes

Symptom Likely cause What to do
Browser detection selects the wrong path A compatibility token or changed string matched your rule Test the required feature; if a workaround is unavoidable, narrow and document the match.
The header is missing from a request The client is configured not to send it, or an intermediary changed the request Inspect the request at the server boundary and check client or proxy configuration; do not assume the header is guaranteed.
A browser or device detail is generic or absent The client reduced the value or chose not to provide the detail Do not infer a specific model or version; use a supported Client Hint only when the detail is needed.
A custom header fails in browser JavaScript Browser APIs may restrict forbidden request headers, and cross-origin requests may require CORS permission Check the browser console and the relevant Fetch/CORS behavior; perform controlled header tests with a server-side client such as cURL or Python where appropriate.
A Client Hint is not returned The browser may not support it, may not honor the request, or may not provide that value Check current compatibility and treat the hint as optional; retain a sensible fallback.

Performance, reliability, and cost notes

  • Performance: reading a request header or checking a feature is inexpensive in ordinary application code. Avoid elaborate parsing and large browser-specific rule sets that are costly to maintain.
  • Reliability: a client-provided string is useful context, not a guarantee. Keep pages functional when the header or a hint is missing, reduced, or unfamiliar.
  • Privacy: collect only the client details needed for a documented purpose and limit retention according to your application’s policy.
  • Cost: the header itself has no special per-read charge in typical server code; operational costs come from the logging, storage, analytics, and maintenance you build around it.

Or skip the browser setup

If the practical task is capturing a rendered page while checking how a site behaves, ScreenshotNeo is a website screenshot API and MCP server for developers. Its screenshot request can set a user agent and viewport, then return an image or PDF. It is useful when you need the rendered result rather than only the request header.

One GET request captures a page. See the ScreenshotNeo API documentation for request parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
  • Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Response headers report the page verdict and billing status.
  • An MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
  • The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is on every plan.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

FAQ

Is the user agent the person using the browser?

No. It is the client software making the request.

Should I use User-Agent sniffing for responsive layouts?

Usually not. Use responsive layout features and capability checks that answer the actual design or behavior question.

Does a reduced User-Agent mean the browser is hiding everything?

No. Reduction limits selected details in supporting browsers. Other request information and browser behavior are separate, and implementations differ.

Are Client Hints always available?

No. They depend on browser support, configuration, and the particular hint requested. Treat them as optional input.