What a Frozen User-Agent String Means for Feature Detection
Chrome has announced plans to freeze its User-Agent (UA) string, and the other major browser vendors are on board with the change. Browsers will still send a UA string in headers and expose it via navigator.userAgent, but the value will become static over time, making it progressively less useful for identifying the browser, platform, or version. The stated motivation is privacy-focused: reducing the fingerprinting surface that UA strings currently provide.
The long-standing guidance in front-end development has been to avoid UA sniffing altogether. The reasoning is straightforward: UA-based logic is frequently implemented incorrectly, and the resulting behavior often causes more harm than the detection was meant to prevent. The recommended alternative is to test for the actual capability you need rather than inferring it from a browser identity.
Testing Features Directly
In JavaScript, capability checks are often trivial. If you need to know whether an API exists, test for its presence:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
console.warn("Geolocation not supported");
}
CSS has a native equivalent through @supports:
@supports (display: grid) {
.main {
display: grid;
}
}
That declaration is mirrored in JavaScript with an API that returns a boolean:
CSS.supports("display: flex");
Not every platform capability is this simple to probe, but the general approach is usually workable without resorting to UA sniffing. When a test proves difficult, Modernizr is worth checking first—it is the de facto standard for feature detection and has been battle-tested against edge cases you might not anticipate. When you use it, the library provides clean logical branches:
if (Modernizr.requestanimationframe) {
// supported
} else {
// not-supported
}
When You Truly Need Browser Identity
If browser type, platform, or version is genuinely required, that information will still be available through User-Agent Client Hints (UA-CH). To retrieve the platform, you set a Sec-CH-Platform header on the request and receive the answer in the response. The requirement to explicitly ask for the data is what ostensibly mitigates the privacy concerns around passive fingerprinting. There is also a Sec-CH-Mobile header for mobile detection, though the definition of “mobile” and the decisions downstream from that flag remain open questions.
Server-side code often faces a harder problem than client-side JavaScript. Unlike a browser environment where you can run tests directly, backend systems must decide what to send before any capability checks execute. Frozen UA strings will retain their usefulness for a transition period, which should give server-side applications time to migrate to UA-CH.
Not everyone is confident the transition will be smooth. Jon Arne Sæterås, who has worked in the mobile web space for over 15 years, argues that device detection is deeply embedded in backend infrastructure:
Functionality based on device detection is critical, widespread and not only in front end code. Huge software systems with backend code rely on device detection, as well as entire infrastructure stacks.
A Practical Example
In one major codebase, the server does a small amount of UA detection using a Rails gem called Browser, which exposes UA-derived data through a clean API:
if browser.safari?
end
The same gem’s output is also surfaced client-side. Across both front and back ends, usage is limited to a handful of instances, none of which look particularly difficult to handle another way.
Historically, getting client environment details to the server on first load has been awkward. The UA string carries no viewport or screen size, so a common workaround involved serving a skeleton page that runs a tiny script—measures viewport width and screen dimensions, sets a cookie, then forces a reload. When the cookie is present, the server skips the skeleton and serves the real page with the client information already available. That made it possible to send different HTML or smaller assets for small screens from the server side.
UA-CH should not be confused with standard Client Hints. With regular Client Hints, servers send an Accept-CH header and then whitelist the values the client is allowed to return:
<meta http-equiv="Accept-CH" content="DPR, Viewport-Width">
That allows a server to receive information about things like viewport dimensions on subsequent page loads. It’s a clean API in theory, but Firefox and Safari do not currently support it. With both vendors signaling interest in UA-CH as part of the frozen UA string effort, the broader Client Hints feature may see renewed adoption.



