Trouble happens when you update a version number

In our third chapter of “The problem with User-Agent strings” we taking a look at the problems you run into when you change something in the User-Agent string.

As I mentioned in the previous instalment of this series, the User-Agent string is built upon layers and layers of lies. Sometimes, when I look at a modern User-Agent string, I feel like an archaeologist digging through the layers and determining what happened by looking at the context.

Despite those lies, we used to be able to tell quite a lot from a User-Agent string. But that has changed in the last couple of years.

I am writing this article on an Apple MacBook Pro that runs macOS 14.5 Sonoma. Yet when I look at the User-Agent string of my browser, it says this:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15

It gives me the right browser—Safari 17.5. But I am not using macOS 10.15 Catalina. So what is going on here?

But it is not just with Safari. If I look at the User-Agent string of Chrome on my Mac, it does precisely the same thing:

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

Again, it says I am using macOS 10.15. Firefox and Edge say precisely the same thing.

First of all, during the beta cycle of macOS 11, Safari initially shipped with a User Agent string that said it was macOS 111. However, very quickly, it became clear that games built with Unity did not work anymore. Not that there was an actual issue, but Unity checked for a minimum supported version of macOS in some naive way, which broke. And Unity wasn’t the only one.

Yes, the way they checked the version of macOS was naive but also understandable. For almost two decades, the major version number of macOS has been 10. During that time, with every update, they increased the minor version from 10.0 to 10.15. And then, Apple suddenly decided to update the major version from 10.15 to 11.0.

The User-Agent string always contained the version with the major and minor components separated by an underscore, like 10_4_0, 10_10_0, 10_15_6, and now it was 11_0_0. So if you do not check the whole version number but only the minor version—which worked fine up to now—your check either fails in its entirety because it can’t find a version starting with 10_, or it ignores the 11_ and thinks you are running Mac OS X 10.0.

This is what Unity did:

switch (os) {
    case "Mac OS X":
      osVersion = /Mac OS X (10[\.\_\d]+)/.exec(nAgt)[1];

Yeah, it broke and before Big Sur was released to the public Apple decided to cap the maximum version to 10.15.6. Unity suddenly worked again. After the release of macOS 11 Big Sur, the other browsers ran into the same thing, and also quickly had to cap their User-Agent string to 10.15.6.

But wait… 10.15.6? The User-Agent string above says 10.15.7. 

Yes, that is true. Apple then released another bugfix update for macOS 10.15 Catalina, 10.15.7, which meant macOS 10.15 had a higher version number than macOS 11 Big Sur. That was weird, and after a while, browsers were updated to always use 10.15.7.

Now, there were other options to fix this. They could continue counting. There is no reason why they could not use 10.16 for macOS 11, 10.17 for macOS 12 and so on. Or they could always prefix the actual version number with 10.15. So for macOS 11 and for macOS 12. Both schemes would have kept the ability to detect the operating system version while, at the same time, it would have fixed the backward compatibility issue. But they chose not to do that and used a fixed version number instead. 

This problem is of course not new. We’ve seen this issue many times before in the history of the User-Agent string. 

When Opera released an alpha version of Opera 10, the developers ran into the same problems. Websites detected the new browser as Opera 1 – because up to now the version number was always a single digit. So you just have to check the first digit. No way web developers could have foreseen that Opera would eventually move to two digits, right? Anyway, Opera used a third method to work around the compatibility issues.

Opera/10.00 (Windows NT 5.1; U; en) Presto/2.2.0

They solved it by using a fixed version number, too. 

Opera/9.8 (Windows NT 5.1; U; en) Presto/2.2.0 Version/10.00

But at least they used a version number that was not in use. Opera 9.8 does not exist, so there is no confusion. In addition to that, they added a new version token containing the actual version number. 

But that did not happen for macOS. We’ve simply lost the ability to determine the operating system’s version number. 

We can deduce a range based on the browser version. Apple releases a new version of Safari with each new major release of macOS, and that version is also made available for the previous two versions of macOS. Safari 14 shipped with Mac OS 11 Big Sur and was later released as a standalone update for macOS 10.14 Mojave en macOS 10.15 Catalina.

Safari 14macOS 10.14, 10.15 and 11
Safari 15macOS 10.15, 11 and 12
Safari 16macOS 11, 12 and 13
Safari 17macOS 12, 13 and 14

It is easy to figure out why this issue was never properly fixed. It happened right in the middle of a big push from browser vendors to prevent fingerprinting, and one way to do that is through user-agent reduction. 

We’ll talk about this effort in the next part of this series.

Continue with User-Agent reduction

  1. Actually, the first beta of macOS 11 used the version 10.16.0, because apparently the decision to change the version to 11 came quite late and macOS 11 was initially developed to be just the next iteration in the 10.x series of releases. The second beta used the version 11.0.0. ↩︎