用户代理检测(User-agent Dtection)通过检测用来代理字符串来确定实际使用的浏览器。每一次 HTTP 请求过程中,用户代理字符串作为首部发送的,该字符串可以通过 JS 的 navigator.userAgent 访问。

在服务器端,通过检测用户代理来确定用户使用的浏览器是一种常用而且广为接受的方法。但由于历史原因,浏览器会在自己的用户代理字符串中加入一些错误或误导信息,因此在客户端,应该优先使用能力检测或怪癖检测,用户代理检测是最后一个选择,一般适用于以下情形:

  • 不能直接准确地使用能力检测或怪癖检测。例如,某些浏览器实现了为将来功能预留的存根(stub)函数,测试相应函数是否存在得不到足够的信息。
  • 同一款浏览器在不同平台下具备不同的能力,有可能需要确定浏览器处于哪个平台。
  • 跟踪分析等目的需要记录浏览器信息。

1. 用户代理字符串的历史

HTTP 规范规定浏览器应该发送简短的用户代理字符串,表明浏览器的名称和版本号,字符串格式为:标示符/产品版本号。但是,现实中的用户代理字符串要复杂得多。

1.1 早期浏览器

1993年,美国 NCSA 发布了世界上第一款 Web 浏览器 Mosaic,其 UA 字符串为: Mosaic/0.9

Netscape 进入浏览器开发领域后,将其产品代号定为 Mozilla (Mosaic Killer)。该公司第一个公开发行版本的 UA 字符串格式为:

Mozilla/Version [Language] (Platform; Encryption)

其中 Encryption(加密类型)可能的值为 U(128位加密)、I(40位加密)、N(未加密)。如:Mozilla/2.02 [fr] (WinNT; I)

1.2 Netscape 3 和 IE3

1996 年发布的 Netscape Navigator 3 超越 Mosaic 成为最流行的浏览器,其 UA 字符串格式调整为: Mozilla/Version (Platform; Encryption [; OS-or-CPU description])

如:Mozilla/3.0 (Win95; U) 为运行在 Win95 下的 Netscape Navigator 3,操作系统和 CPU 信息被省略。

IE3 在 Netscape Navigator 3 之后发布,为了通过针对 Netscape Navigator 3 的探测代码、争夺市场,微软在 IE 的 UA 字符串中加入了 Mozilla 字样,格式为:

Mozilla/2.0 (compatible; MSIE Version; Operating System)

当时的大多数浏览器探测程序只检测 UA 字符串的产品名称部分,IE3 成功伪装成 Netscape Navigator。

1.3 Netscape 4 和 IE4-10

1997 年,Netscape 4 发布,遵循前一版的 UA 字符串格式;发布补丁时,相应提高子版本号。如 Mozilla/4.79 (Win98; I)

IE4 发布时,微软也将伪装版本号改为4,格式如下:

Mozilla/4.0 (compatible; MSIE Version; Operating System)

直到 IE8,这个伪装版本号都没有变,如 IE7 是:Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)

IE8 的 UA 字符串添加了渲染引擎 Trident 及其版本号:

Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)

因为 IE7 UA 字符串中没有 Trident 版本号,开发人员可以以此区分 IE7兼容模式下的 IE8

IE9 和 IE10 的 Mozilla 版本号增加到 5.0,Trident 版本号分别为 5.0 和 6.0。IE9 兼容模式下 MozillaMSIE 版本号恢复旧值,Trident 版本号保持不变, IE7 模式中仍然有 Trident 版本号(没有 IE9 无法实测)。IE10 兼容模式下Mozilla、MSIE、Trident 版本号都会恢复旧的值,IE7 模式则没有 Trident 版本号,另外 IE7、IE8 模式的 UA 字符串中还包含 .Net 版本信息。

标准模式下的 IE9 、 IE10 UA:

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)

1.4 Gecko

Gecko 是 Firefox 的呈现引擎,第一个采用 Gecko 引擎的浏览器是 Netscape 6。Netscape 6 规范中规定的 UA 字符串格式为:
Mozilla/MozillaVersion (Platform; Encryption; OS-or-CPU; Language;
                        PrereleaseVersion)Gecko/GeckoVersion
                        ApplicationProduct/ApplicationProductVersion
比如:
// Netscape 6.21 on Windows XP:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128 Netscape6/6.2.1

// SeaMonkey 1.1a on Linux:
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1b2) Gecko/20060823 SeaMonkey/1.1a

// Firefox 2.0.0.11 on Windows XP:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11

// Camino 1.5.1 on Mac OS X:
Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.6) Gecko/20070809 Camino/1.5.1


Mozilla 版本号都为 5.0,自从第一个基于 Gecko 的浏览器到现在的 Firefox 20.0 都没变。随着 Firefox 4.0 发布,Mozilla 简化了 UA 字符串:删除语言标记、强加密(默认设置)时不显示加密类型、删除平台标记、Gecko 版本号固定为 Gecko/20100101。下面是 Firfox 20.0 的 UA 字符串:

 Mozilla/5.0 (Windows NT 6.2; rv:20.0) Gecko/20100101 Firefox/20.0

1.5 Webkit

2003 年,Apple 宣布要发布自己的浏览器 Safari,呈现引擎 Webkit 是基于 KHTML 的一个分支。几年后,Webkit 独立出来成为一个开源项目,专注于呈现引擎开发。

和 IE3 发布时一样,为了不被流行站点的探测脚本 PASS 掉,Safari 的 UA 字符串加了足够多的兼容信息,格式如下:

Mozilla/5.0 (Platform; Encryption; OS-or-CPU; Language)
AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Safari/SafariVersion

// 示例Win8 上的 Safari 5 精简了一些信息
Mozilla/5.0 (Windows NT 6.2) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
// Chrome 26,和 Safari 很像吧
Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31

1.6 Konqueror

与 KDE Linux 集成的 Konqueror,是一款基于 KHTML 的浏览器。
Mozilla/5.0 (compatible; Konqueror/Version; OS-or-CPU) KHTML/KHTMLVersion (like Gecko)

// 示例
Mozilla/5.0 (compatible; Konqueror/3.5; SunOS) KHTML/3.5.0 (like Gecko)

1.7 Chrome

Chrome 以 WebKit 作为呈现引擎(要换 Blink 了),不过使用了不同 JavaScript 引擎。其 UA 字符串和 Webkit 差不多,看上面的例子。

1.8 Opera

Opera 的 UA 字符串相对较守规矩:
// 8.0 之前的 UA
Opera/Version (OS-or-CPU; Encryption) [Language]
// Opera 7.54 on Windows XP
Opera/7.54 (Windows NT 5.1; U) [en]

// Opera 8
Opera/Version (OS-or-CPU; Encryption; Language)
// Opera 8 on Windows XP
Opera/8.0 (Windows NT 5.1; U; en)


Opera 9 可以将自己标识为 IE 或者 Firefox:

// 标识为 FF
Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
// 冒牌 IE
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50

上面的模仿方式至少末尾还有 Opera 和版本号字样,Opera 还可以删除末尾的 Opera 字样,返回和 IE、Firefox 完全一样的 UA 字符串(坑爹啊)。

Opera 10 对代理字符串进行了修改,现在的格式是:

Opera/9.80 (OS-or-CPU; Encryption; Language) Presto/PrestoVersion Version/Version

Opera/9.80 固定不变,据说是怕蹩脚探测脚本把 Opera/10.0 错误解释为 Opera 1;还增加了呈现引擎 Presto 及版本号。(现在要改用 Webkit 了,不过人家 Google 自己玩 Blink 去了,Opera 你怎么看?)

1.9 iOS 和 Android

iOS 和 Android 的默认浏览器都基于 Webkit,他们共享同样的 UA 字符串。iOS 设备的 UA 字符串格式为:
Mozilla/5.0 (Platform; Encryption; OS-or-CPU like Mac OS X; Language) AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Version/BrowserVersion Mobile/MobileVersion Safari/SafariVersion

// iPhone 5 6.1.2 加密方式、语言被删除
Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_2 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B146 Safari/8536.25

// iPad Mini
Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A406 Safari/8536.25


平台可能是 iPhone、iPad(Mini也是返回iPad)、iPod;iOS 3 之前不会出现系统版本号。

Android 默认浏览器的 UA 字符串与 iOS相似,没有移动版本号但有 Mobile 标记。

// Google Nexus One UA
Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1

2. UA 字符串检测技术

通过 UA 字符串检测浏览器并不是一件轻松的事。一般情况下,知道呈现引擎和最低限度的版本就足以解决正确的方法。

UA 字符串检测分为以下几个部分:

  1. 识别呈现引擎
  2. 识别浏览器
  3. 识别平台
  4. 识别操作系统
  5. 识别移动设备
  6. 识别游戏设备
首先创建一个对象字面量,将要检测的部分都放到里面,开始检测之旅。
var client = function(){
//rendering engines
var engine = {
    ie: 0,
    gecko: 0,
    webkit: 0,
    khtml: 0,
    opera: 0,

    //complete version
    ver: null
};

//browsers
var browser = {

    //browsers
    ie: 0,
    firefox: 0,
    safari: 0,
    konq: 0,
    opera: 0,
    chrome: 0,

    //specific version
    ver: null
};

//platform/device/OS
var system = {
    win: false,
    mac: false,
    x11: false,

    //mobile devices
    iphone: false,
    ipod: false,
    ipad: false,
    ios: false,
    android: false,
    nokiaN: false,
    winMobile: false,

    //game systems
    wii: false,
    ps: false
};

// 探测代码

//return it
return {
    engine:     engine,
    browser:    browser,
    system:     system
};

}();

2.1 识别浏览器呈现引擎

主要检测的5大呈现引擎:IE、Gecko、WebKit、KHTML、Opera。要正确识别呈现引擎,关键是按正确的顺序检测,否则可能导致结果不正确。
  1. 第一步:通过检测 window.opera (5+存在这个对象)识别 Opera,调用 version() 方法(7.6+)返回浏览器版本字符串;
  2. 第二步:通过 AppleWebKit 这个独特的字符串检测 WebKit;
  3. 第三步:使用 KHTML 字符串检测 KHTML 引擎,Konqueror 3.1既及更早版本中不包含 KHTML 版本,所以还要加上 Konqueror;
  4. 第四步:检测 Gecko
  5. 第五步:检测 IE,其版本号位于 MSIE 后面,一个分号前面。

2.2 识别浏览器

对于 Opera 和 IE 而言,browser 对象中的值等于 engine 对象中的值。提取 Chrome 版本号时,需要查找字符串 Chrome/ 并取得该字符串后面的数值;提取 Safari 版本号时,需要查找 Version/ 并取得其后的数值(仅适用于 Safari 3+)。检测 Firefox 时,查找 Firefox/ 提取后面的数值。

2.3 识别平台

三大主流平台:Windows、Mac、Unix(包括各种 Linux)。

确定平台时,检测 navigator.platform 要比检测用户代理更简单,可能值为 Win32、Win64、MacPPC、MacIntel、X11、Linux i686。

var p = navigator.platform;
system.win = p.indexOf("Win") == 0; // 32位、64位都返回Win
system.mac = p.indexOf("Mac") == 0; // PPC、Intel 都返回 Mac
system.x11 = (p.indexOf("X11") == 0) || (p.indexOf(“Linux”) == 0); // Unix

2.4 识别 Windows 操作系统

 
不同浏览器在不同 Windows 版本中的字符串
WINDOWS IE4+ GECKO OPERA<7 OPERA7+ WEBKIT
95 Windows95 Win95 Windows95 Windows 95 n/a
98 Windows98 Win98 Windows98 Windows 98 n/a
NT4.0 WindowsNT WinNT4.0 WindowsNT4.0 WindowsNT4.0 n/a
2000 WindowsNT5.0 WindowsNT5.0 Windows2000 WindowsNT5.0 n/a
ME Win9x4.90 Win9x4.90 Windows ME Win9x4.90 n/a
XP WindowsNT5.1 WindowsNT5.1 Windows XP WindowsNT5.1 WindowsNT 5.1
Vista WindowsNT6.0 WindowsNT6.0 n/a WindowsNT6.0 WindowsNT 6.0
7 WindowsNT6.1 WindowsNT6.1 n/a WindowsNT6.1 WindowsNT 6.1
8 WindowsNT6.2 WindowsNT6.2 n/a WindowsNT6.2 WindowsNT 6.2
// 匹配 Windows 95 和 Windows 98: Gecko没有 dows,Win 和版本号之间没有空格
// 版本信息中除了信息,还可能是 NT、ME、XP,所以使用两个非空字符
/Win(?:dows )?([^do]{2})/

//Gecko 在标识 NT 时会在末尾添加 4.0, (\d+.\d+)?
/Win(?:dows )?([^do]{2})(\d+.\d+)?/

// \s?:Opera 表示 Windows NT时,NT 和 4.0 中间有空格
/Win(?:dows )?([^do]{2})\s?(\d+.\d+)?/

2.5 识别移动设备

iOS设备:
system.iphone = ua.indexOf("iPhone") > -1;
system.ipod = ua.indexOf("iPod") > -1;
system.ipad = ua.indexOf("iPad") > -1; // iPad,包括 Mini

//determine iOS version
if (system.mac && ua.indexOf("Mobile") > -1){
if (/CPU (?:iPhone )?OS (\d+\d+)/.test(ua)){
system.ios = parseFloat(RegExp.$1.replace("
", "."));
} else {
system.ios = 2; // iOS 3之前 iOS 系统版本号,只能猜测
}
}


检测 Android 只有搜索 Android 字符串并取得后面的版本号即可。Nokia N 系列手机默认浏览器也基于 WebKit,但只要检测用户代理字符串是否包含 NokiaN 即可。

// determine Android version
if (/Android (\d+\.\d+)/.test(ua)){
    system.android = parseFloat(RegExp.$1);
}

// Nikia N
system.nokiaN = ua.indexOf("NokiaN") > -1;

区分不同的 WebKit 移动浏览器:

if (client.engine.webkit){
    if (client.system.ios){
      //iOS stuff
    } else if (client.system.android){
      //android stuff
    } else if (client.system.nokiaN){
      //nokia stuff
    }
}

Windows Mobile 及 Windows Phone:

//Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; PPC; 240x320)
//Mozilla/4.0 (compatible; MSIE 4.01; Windows CE; Smartphone; 176x220)
//Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0) Asus;Galaxy6

// windows mobile
if (system.win == "CE"){
    system.winMobile = system.win;
} else if (system.win == "Ph"){
    if(/Windows Phone OS (\d+.\d+)/.test(ua)){;
        system.win = "Phone";
        system.winMobile = parseFloat(RegExp["$1"]);
    }
}

2.6 识别游戏设备

Wii 中的浏览器为 Opera 定制版;PlayStation 的浏览器是自己开发的,没有基于前面提到的呈现引擎。
// Wii UA: Opera/9.10 (Nintendo Wii;U; ; 1621; en)
// PS UA: Mozilla/5.0 (PLAYSTATION 3; 2.00)
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);

完整代码

Github gist