Should we rely on browser detection?

This is the sixth and final chapter of “The problem with User-Agent strings “. In the previous chapters, we’ve learned that the User-Agent string is something that web developers should handle with care. Handle with extreme care because it could blow up in your face at any time. But if you knew what you did, you could use it to get somewhat reliable information. We’ve also learned this changed during the last few years due to User-Agent reduction and compatibility issues. But at least there is a modern replacement: User-Agent Client Hints.

In this chapter, we’ll examine the current state of browser detection, whether it is accurate, and whether we should rely on it.

Safari and Firefox do not support User-Agent Client Hints

The first obstacle we will encounter is the need for more browser support for the User-Agent Client Hints API. Yes, Chrome, Edge and other Chromium-based browsers support it, but that is it. 

So, while we can now tell the operating system version on Chrome or Edge, we still cannot tell when the user is using Safari or Firefox. We can build a download page that selects the right build to download for Chromium browsers, but we still have to create an alternative page where the user manually selects the right build for other browsers. 

Analytics are not reliable anymore 

Some analytics packages – like Webalizer, or Awstats – are server-side analytics. They do not use tracking cookies or other potentially privacy-invading methods to collect information. They parse the web server logs, and that is it. On most servers, an automatic cronjob will delete those logs after several days, so only the anonymised aggregated stats remain. This is probably the most privacy-friendly method of analytics.

These packages parse the logs, but what data does the web server store in the logs? The reduced User-Agent string. As discussed in previous chapters, the reduced User-Agent string only contains the browser name, version, and operating system name, with other details replaced by fake information. So, if you use Awstats, all your macOS users will forever use version 10.15.7. Do you use Webalizer? Your visitors will never upgrade to Windows 11.

But it is not just server-side analytics. Even if your analytics package was updated to use the User-Agent Client Hints API, it will still underreport Windows 11 usage because Firefox will still report Windows 11 as Windows 10. Similarly, macOS 10.15 will be overreported because Safari will not reveal the actual version of the operating system.

Are User-Agent Client Hints reliable?

When User-Agent Client Hints were first proposed, I liked how they would provide a clean slate for browser detection. Finally, we would no longer have to deal with those lies just for the sake of backward compatibility. But I also remembered how we got here and feared it was just a matter of time before we’d screw this one up too.

So, four years later. How accurate are the User-Agent Client Hints? 

Let’s look at the brands for some common Chromium-based browsers, both on desktop, mobile, and on other devices. In the table below, I’ve re-ordered the brands and removed the fake information.

Chrome“Google Chrome”;v=”124″, “Chromium”;v=”124″
Edge“Microsoft Edge”;v=”124″, “Chromium”;v=”124″
Opera GX“Opera GX”;v=”109″, “Chromium”;v=”123″
Brave“Brave”;v=”124″, “Chromium”;v=”124″
Samsung“Samsung Internet”;v=”25.0″, “Chromium”;v=”124″
Oculus Quest 2“Oculus Browser”;v=”33″, “Chromium”;v=”124″
Vivaldi“Google Chrome”;v=”124″, “Chromium”;v=”124″

Most tested browsers actually populate the brands with the correct information: the actual browser name and version. All of them mention Chromium as well, which makes sense. 

Let’s compare this with the User-Agent string for Brave:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36

The browser’s name is never mentioned in the User-Agent string, so the information we get from the Client Hints is more accurate.

But that is where the good news ends. 

Arc does not have its own brand. It just uses the Chromium brand. That may be the right choice if you are just starting out and have a very limited number of users. Otherwise, you will make it very easy to track users using fingerprinting.

Vivaldi, on the other hand, says it is Google Chrome. Vivaldi is lying. We’ve got our first lie in the User-Agent Client Hints API. Sigh.

One other issue I ran into is the high-entropy data with Brave. All data is available using the JavaScript API, but that is not the case when you request the high-entropy data using headers. There is one exception: the platform version is available with both methods. Weird.

Desktop mode complicates things even further

Mobile browsers and desktop browsers are very different. One has a small screen, and the other has a large screen. One is touch-oriented; the other has a keyboard and mouse. One is mobile, the other not. One is battery-based and thus limited in power; the other is not. 

Tablets have always been a weird middle ground. They are mobile and touch-oriented, but they have a big screen. They use the same operating system as mobile devices, but their capabilities are, in some ways, closer to desktop machines than mobile phones.

Now consider that some web developers use the User-Agent string to switch between mobile and desktop versions of their websites. How would one do this? Safari and Chrome (and other browsers) include the token Mobile in the User-Agent string. 

Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Mobile Safari/537.36

Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1

If you use User-Agent Client Hints, there is also a property mobile to indicate a mobile browser. 

Sec-Ch-Ua-Mobile: 1

Looking at other information is asking for trouble. Unfortunately, that is precisely what developers did. They tested for Android or like Mac OS X. Yes, that will match mobile devices like Android phones and iPhones, but it will also match tablets and other devices.

Nowadays, tablets use “desktop mode” by default. “Desktop mode” simply means using a desktop version of the User Agent string. They have to because otherwise, they will get the mobile versions of the website. And it’s not just tablets. It’s all kinds of devices running Android1. And even phones usually offer an optional “desktop mode.” 

Chrome on an Android tablet suddenly looks like this:

Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/ Safari/537.36

It is not just the User-Agent string; the Client Hints also lie.

Sec-CH-UA-Platform: "Linux"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Arch: "x86"

As of Chrome 124, there is a new Form-Factors client hint that at least tells us we’re dealing with a tablet, or any other type of device, like a VR headset2. At least we know how to differentiate between desktop mode on a tablet and Chrome on Linux.

Sec-CH-UA-Form-Factors: "Tablet"

Ehhhh… no. I tested this on the Pixel Tablet with Chrome 124 and regardless of “Desktop mode” being enabled or disabled, this is what the header actually says:

Sec-CH-UA-Form-Factors: "Desktop"

Sigh… Chrome 124 on Mobile actually does say Mobile. What use is this header if it doesn’t really tell us what the form factor is… We could just use Sec-CH-UA-Mobile instead.

Again, we’re heading towards the lies and the tricks we needed for the User-Agent string. 

It’s the same old story we’ve seen so many times. 

Opera/9.80 (X11; Linux zbov; U; en) Presto/2.9.201 Version/11.50

At first glance, the User-Agent string above looks like Opera 11.50 on Linux. But if you ROT13 decode the string zbov, it says mobi. Yes, this is Opera Mini in desktop mode. This trick is at least 15 years old.

Same old, same old.

Should we rely on browser detection?

The short answer is no. Browser detection, either using the User-Agent string or client hints, is a guess. In most cases, it’s a good guess, but a guess nonetheless. 

There are cases where guessing is acceptable, such as simple analytics. Some browsers and operating systems may be under or overreported, but that is fine in most cases.

In web apps, you can use telemetry to find and log errors. The User-Agent string and browser detection are helpful because you can trace problems that only happen in certain browsers or versions.

We’ve discussed a download page where CPU architecture and bitness detection are helpful. But even in those cases, you might want to offer two ways to download the application: the automatic way and the manual way, for when detection fails or the user wants to download the software for use on another machine.

But in most cases, you don’t need browser detection. Browser detection is usually a sign of something else going wrong. 

Instead of building two websites, one for mobile and one for desktop, consider creating one website using a responsive layout. If that is not possible, try to keep your browser detection logic as simple as possible. 

When you are worried about users using outdated browsers, use feature detection instead of keeping a list of supported browsers and blocking earlier versions. Don’t block browsers based on their User-Agent string or client hints. That will only lead to false negatives and false positives. If you really, really need to block browsers, do so based on standards support. Or even better, don’t block but use progressive enhancement wherever possible.

In most cases, you don’t need to know your users’ browser or type of system. Build your sites and web apps for the web platform, not for a single browser. 

  1. VR headsets like the Apple Vision Pro and the Meta Oculus also use “desktop mode” by default ↩︎
  2. I’ve tried Oculus Browser 33, which is based on Chromium 124, but the header does not give any information. It’s early, so hopefully it will be properly implemented soon. ↩︎

This article is best viewed with: