Browser Detection. Or, to IE or not to IE?
This document is not intended to be an in-depth treatise on the history of browser detection or browser content negotiation, and much of this information can be found freely available elsewhere. It's purpose here is to serve as a quick background to some of the other test pages on this site.
Background
Browsers send a ‘user agent’ string to the server, which identifies several characteristics of the browser itself, and the platform upon which it's running. This string can be detected using both client-side (e.g. JavaScript) and server-side (e.g. PHP, ASP) scripting methods, allowing content to be tailored to the browser's capabilities.
For example, the browser that you're currently using describes itself as follows:CCBot/1.0 (+http://www.commoncrawl.org/bot.html)
Some other user agent strings are given below.
- Internet Explorer 6.0 on Windows XP
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)
- Internet Explorer 5.5 on Windows 2000
- Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)
- Internet Explorer 5.0 on Windows 98
- Mozilla/4.0 (compatible; MSIE 5.0; Windows 98)
- Firefox 1.5 on Windows XP
- Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4
- Safari 2 on Mac OS X 10.4
- Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418 (KHTML, like Gecko) Safari/417.9.2
- Firefox 1.5 on Mac OS X 10.4
- Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
- Opera 8.5 on Mac OS X 10.4
- Opera/8.5 (Macintosh; PPC Mac OS X; U; en)
- iCab 3 (beta 382) on Mac OS X 10.4
- iCab/3.0.2 (Macintosh; U; PPC Mac OS X)
- Mozilla/5.0 (compatible; iCab 3.0.2; Macintosh; U; PPC Mac OS X)
- Camino 1.0.1 on Mac OS X 10.4
- Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.3) Gecko/20060427 Camino/1.0.1
- Internet Explorer 5.2.3 on Mac OS X 10.4
- Mozilla/4.0 (compatible; MSIE 5.23; Mac_PowerPC)
In the early days of graphical web development, the user agent string was used to identify specific browsers—‘browser sniffing’—and target content delivery according to their support for specific methods and objects, many of which were proprietary. As Netscape Navigator stagnated at version 4, and Internet Explorer development continued to better versions, many sites targeted this browser to develop sites with an enriched user experience.
These days, browser detection is generally considered to be a bad thing. But there are plenty of sites out there that have not been updated to account for progress in browser development. Worse still, there are some sites being developed that reply on browser detection.
Why is browser detection bad?
The obvious, but not only, problem with browser detection is that browsers are constantly being developed. What a given browser does and does not support now may not be true in a year's time.
Another problem lies with the wide variety of browsers out there. As some cease development, others spring up. Therefore, not only must a browser detect account for the changing capabilities of new browser versions, but it must also take into account all browsers. And that's no mean feat!
Therefore, browser detectors are often outdated unless they're constantly updated.
Finally, there is the issue that browsers aren't always what they claim to be…
Welcome to the masquerade
So, what happens when poorly coded sites block access by non-Microsoft browsers? Well, some of them can masquerade as Internet Explorer by sending a falsified user agent response, thereby circumventing the block. These are the user agent strings returned for three browsers that have been set to identify themselves as IE6.
- Safari 2 on Mac OS X 10.4
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)
- Opera 8.5 on Mac OS X 10.4
- Mozilla/4.0 (compatible; MSIE 6.0; Mac_PowerPC Mac OS X; en) Opera 8.5
- iCab 3 (beta 382) on Mac OS X 10.4
- Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2)
Opera adds an identifier to the end of the user agent string (‘Opera 8.5’ in this case), to differentiate it from the real Internet Explorer and other browsers masquerading as Internet Explorer, and retains the operating system identity. Consequently, testing for whether a browser is truly Internet Explorer or Opera masquerading as Internet Explorer is simply a case of searching for ‘Opera’ in the user agent string. In PHP, it would look something like this:
The JavaScript equivalent would be:
In contrast, both Safari and iCab not only lose all identity, retaining no browser-specific string in the user agent response, they even proclaim themselves to be Windows-based. This makes sense in that Microsoft discontinued support of Internet Explorer for the Mac in 2003 at version 5.2.3, and it often behaved differently to its Windows-based equivalent anyway. But it makes it difficult to differentiate between these browsers and the real Internet Explorer.
As far as I can see, the only way to differentiate between these browsers masquerading as IE6 and genuine IE6 are the ‘Windows NT’ and ‘.NET CLR’ strings. The former is given as ‘Windows NT 5.2’ on Mac OS X-based systems and Windows Server 2003, and as ‘Windows NT 5.1’ on Windows XP-based systems.
Windows Server 2003 with the .NET framework installed adds the ‘.NET CLR’ string, and this can be used to differentiate it from Mac OS X. The limitation will be, of course, the differentiation of genuine IE6 on Windows Server 2003 without the .NET framework from ‘IE6’ on Mac OS X. Here I make an assumption: that anyone technophilic enough to be using Windows Server as a basis for a web surfing platform will also have installed .NET!
Windows 2000, Windows XP, and Windows Server 2003 are based on versions 5.0, 5.1, and 5.2 respectively of the NT kernel. Windows Vista is version 6.0, but it will be running IE7. So, it seems that a reasonable, albeit not foolproof, way of differentiating IE6 from browsers masquerading as it, would be to search for the version numbers of both the ‘MSIE’ and ‘Windows NT’ strings, and the presence of the ‘.NET CLR’ string.
Pulling this all together, the PHP browser detect would look like this:
The JavaScript equivalent would be:
How should we target browsers with different levels of functionality?
The answer is simple (most of the time): object detection!
Object detection is the principle of testing for a browser's support for a particular object or method to serve appropriate code, at the same time preventing browsers that don't have such support from throwing endless errors. This is far superior to detecting the name and version number of a browser, as it detects the ability of a browser to support the scripts that you wish to use.
At once this addresses the two most significant problems associated with browser detection: it's future-proof and requires no further maintenance; it covers all browsers in current or future use.
For example, in order to test that a browser supports the W3C document object model, we can test to see if the browser supports the getElementById method of the document object:
Since earlier browsers supported only parts of the W3C DOM, we might want to hide more advanced scripting from them by checking for their support for other methods:
When could browser detection be used?
There are times when browser detection might be useful, for example where a specific browser must be targeted or where object detection is not feasible.
Object detection relies on testing a browser's support of objects and methods, and is therefore restricted to client-side scripting methods (e.g. JavaScript.) Object detection may not be possible for some objects/methods, or when server-based scripting methods are employed.
An example of this is the support of 24-bit alpha transparency, or lack thereof in IE6. I haven't found any object reference that usefully differentiates browsers that do support this feature from those that don't. Consequently, the only alternatives are browser detection or conditional comments.
With thanks to Peter Ruijter for error correction!