What is the User-Agent header?
The User-Agent request header, defined in Section 10.1.5 of RFC 9110, identifies the software that originated the request. Its value is one or more product tokens, each a name and optional version, optionally followed by comments in parentheses, listed in decreasing order of significance:
User-Agent: CERN-LineMode/2.15 libwww/2.17b3A user agent should send it on every request unless configured not to. The RFC also sets limits on what goes in it: product identifiers must not carry advertising or other nonessential information, the version part should only ever be a version, and a client should avoid needlessly fine-grained detail because long, specific values increase latency and make users easier to fingerprint.
API Usage & Best Practices
- Send a descriptive one from every SDK. Put your SDK first, its version, then the runtime it is running on:
acme-python/4.2.0 python/3.12.4. When a bug report arrives, or a breaking change needs a rollout plan, the first question is always which client versions are still calling you, andUser-Agentanswers it. - Let integrators append their own product. Give SDK users an option to add an application token, so traffic from
billing-sync/1.3 acme-node/4.2.0 node/22.9.0can be traced to a team rather than just to your library. This is the order RFC 9110 asks for: most significant product first. - Requiring it is reasonable. An API is within its rights to reject requests with no
User-Agent, or with a generic default from an HTTP library, and to ask callers to identify their application. GitHub’s REST API does exactly this. If you do, document it and return a clear400 Bad Requestor403 Forbiddenwith a problem body that says what to send. - Never use it for authentication or authorization. It is a free-text string the client chooses. Anyone can copy your mobile app’s
User-Agent. Identity comes fromAuthorization, not from what a client claims to be. - Do not use it to block abuse on its own. Blocking a
User-Agentstops only the clients that do not change it. Rate-limit on authenticated identity. - Avoid feature-switching on it. Tailoring responses to a parsed
User-Agentcreates behavior that is invisible in your contract and breaks when a client changes its string. If a response genuinely differs by client, version the API or use content negotiation. If you must work around a known client bug, match on the exact product token and version, and remove the workaround once that version is gone. - Do not include personal data in it. No user IDs, email addresses or device serial numbers. It is logged everywhere, by every intermediary.
- Mind browsers. Browsers do not let page JavaScript set
User-Agentreliably, so an API called from browsers sees whatever the browser sends. Do not make it a hard requirement for browser-facing endpoints.
Examples
An SDK request identifying the application, the SDK and the runtime:
GET /v1/invoices HTTP/1.1Host: api.example.comUser-Agent: billing-sync/1.3.0 acme-node/4.2.0 node/22.9.0Authorization: Bearer eyJhbGciOi...A rejected request from an anonymous client:
HTTP/1.1 400 Bad RequestContent-Type: application/problem+json
{ "type": "https://api.example.com/problems/user-agent-required", "title": "User-Agent required", "status": 400, "detail": "Send a User-Agent that names your application, for example 'my-app/1.0'."}