User-Agent Client Hints vs User-Agent Sniffing

Traditional user-agent sniffing has long been a fragile, error-prone method for detecting browser and device capabilities, often leading to broken web experiences and privacy risks. The User-Agent Client Hints (UA-CH) API replaces this legacy approach by offering a structured, privacy-preserving, and deliberate mechanism for servers to request only the specific client information they need. This article explores the shortcomings of legacy user-agent strings, the architecture of User-Agent Client Hints, and how modern web developers can implement them for reliable content negotiation.

The Problem with Legacy User-Agent Sniffing

For decades, browsers communicated their identity through the User-Agent HTTP header. Over time, this single string accumulated historical baggage, with browsers spoofing each other's tokens to bypass arbitrary site blocking. The result was a bloated, unstructured string filled with conflicting identifiers (such as Chrome reporting itself as Safari, Mozilla, and KHTML).

Parsing these strings required complex regular expressions and constantly updated third-party detection libraries. Minor browser updates could alter string formatting, inadvertently breaking site functionality. Crucially, the legacy User-Agent string broadcasted granular system details on every single HTTP request—such as precise operating system builds and device models—making it a primary vector for passive browser fingerprinting.

How User-Agent Client Hints Work

To resolve these issues, major browser vendors began freezing the legacy User-Agent string and introduced User-Agent Client Hints. Rather than sending a monolithic string by default, the browser splits information into two categories: Low-Entropy and High-Entropy hints.

  1. Low-Entropy Hints: Safe, basic information sent automatically with every request. These do not reveal enough detail to uniquely identify a user.
    • Sec-CH-UA: The browser brand and significant version (e.g., "Chromium";v="124", "Google Chrome";v="124").
    • Sec-CH-UA-Mobile: A boolean value indicating whether the browser is running on a mobile device (?0 or ?1).
    • Sec-CH-UA-Platform: The operating system family (e.g., "Windows" or "Android").
  2. High-Entropy Hints: Granular details that could contribute to user tracking, such as exact OS versions, device models, full architecture, or bitness. These are never sent by default.

The Explicit Request Model

The core architectural change in UA-CH is that servers must explicitly opt in to receive high-entropy data. This shift eliminates passive tracking and forces applications to request only what is functional for their use case.

When a server requires high-entropy details, it sends an Accept-CH response header specifying the exact hints it needs:

Accept-CH: Sec-CH-UA-Platform-Version, Sec-CH-UA-Model, Sec-CH-UA-Arch

On subsequent requests to that origin, the browser includes the requested headers:

Sec-CH-UA-Platform-Version: "15.0.0"
Sec-CH-UA-Model: "Pixel 8"
Sec-CH-UA-Arch: "arm"

Accessing Client Hints via JavaScript

Client-side code can access low-entropy hints synchronously through the navigator.userAgentData property, avoiding string parsing entirely:

const { brands, mobile, platform } = navigator.userAgentData;

if (mobile) {
  // Apply mobile-specific logic
}

If the application requires high-entropy data, it must request it asynchronously via getHighEntropyValues(), returning a Promise:

navigator.userAgentData.getHighEntropyValues([
  "architecture",
  "model",
  "platformVersion"
]).then(hints => {
  console.log(hints.platformVersion);
});

Why UA-CH Outperforms Legacy Sniffing

  • Structured Data: Information is delivered as key-value pairs formatted according to the Structured Fields standard, removing the need for fragile regex parsing.
  • Privacy by Default: By withholding detailed device identifiers until explicitly requested, UA-CH drastically limits passive tracking.
  • Resilience: Because hints are explicitly typed and standardized, minor browser patches and upstream engine changes do not unexpectedly break parsing logic.
  • Granular Control: Site owners and third-party services can define permissions policies (using the Permissions-Policy header) to govern which subresources are permitted to access client hints.

By transitioning from passive, monolithic user-agent sniffing to targeted, structured Client Hints, modern web applications gain a more resilient codebase while respecting modern user privacy standards.