User-Agent 是客户端的自我介绍。它有一段荒诞的兼容史——Chrome 自称 Mozilla、WebKit、Safari。
今天它正在变短,别再靠它做业务判断。
19 个请求首部的最后一个。它也是最被滥用、最不可靠的一个。
一、一段兼容史堆出来的字符串
看 Chrome 的 UA,它是这么一长串:
Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/120.0.0.0 Safari/537.36
它同时宣称自己是 Mozilla、是 AppleWebKit、是 Safari。 这三样它都不是——它是 Chrome,跑在 Blink 引擎上。 为什么?早年网站用 UA 做嗅探:不是 Netscape 就给你一个「浏览器不支持」的页面。 于是新浏览器都往字符串里塞前任的名字,骗过那些判断。一代代堆下来,就成了这个四不像。 Demo 里六种 UA 的长度:Chrome 111 字节、Edge 125、Safari 117、Firefox 80、python-requests 22、 curl 10。浏览器那几个都在一百字节上下,每一个请求都要带上。
二、为什么不能靠它做判断
能伪造:任何客户端都能发任意 UA,改一下就是「Chrome」。
会说谎:Edge 的 UA 里既有 Chrome 也有 Safari,你要判断「这是不是 Chrome」,
只能去查有没有 Edg/。而下一个浏览器可能加一个新标记,你的判断又错了。
会变:Chrome 从 2022 年开始推 UA 缩减,把版本号冻成 120.0.0.0,平台信息也简化。
所以依赖精确版本号的判断会逐渐失效。
正确做法是特性检测:想知道对方支不支持某个功能,就去测那个功能,而不是猜它是什么浏览器。
服务端同理,看客户端发的能力首部,别猜 UA。
三、那它还有什么用
统计:看流量里各浏览器的占比,做产品决策。
这个用途不受准确性问题影响,因为只需要大致分布,不需要精确识别。
日志排查:线上出问题时,从日志里看是哪些客户端受影响。即使 UA 可以伪造,正常用户不会去伪造。
礼貌标识:爬虫按规定该在 UA 里写上自己的名字和联系方式,
比如 MyCrawler/1.0 (+https://example.com/bot)。这比 6.4.7 那个死掉的 From 实用得多。
所以结论是:用它做统计和标识可以,做功能判断不行。这条界限要划清楚。
四、动手 Demo:六种客户端的 UA 长度与特征
把六种真实 UA 字符串发过去,服务端原样记录长度,再用关键词扫一遍,看能从中提取出哪些信息、 又会误判什么。
UA_LIST = [
(u'Chrome 120(Windows)', u'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
u'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'),
(u'Edge 120(Windows)', u'... Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0'),
(u'Python requests', u'python-requests/2.31.0'),
(u'curl', u'curl/8.4.0'),
]
kw = [w for w in (u'Windows', u'Macintosh', u'Chrome', u'Safari',
u'Firefox', u'Edg', u'Gecko', u'requests', u'curl')
if w in ua]
实跑输出:
--------------------------------------------------------------------
User-Agent:一个越来越短的字符串
--------------------------------------------------------------------
【Chrome 120(Windows)】
长度 111 字节
内容 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
能看出的关键词 Windows | Chrome | Safari | Gecko
【Edge 120(Windows)】
长度 125 字节
内容 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
能看出的关键词 Windows | Chrome | Safari | Edg | Gecko
【Safari 17(macOS)】
长度 117 字节
内容 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15
能看出的关键词 Macintosh | Safari | Gecko
【Firefox 121(Windows)】
长度 80 字节
内容 Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
能看出的关键词 Windows | Firefox | Gecko
【Python requests】
长度 22 字节
内容 python-requests/2.31.0
能看出的关键词 requests
【curl】
长度 10 字节
内容 curl/8.4.0
能看出的关键词 curl
这张表最值得看的是 Chrome 那一行:它同时宣称自己是 Mozilla、
是 AppleWebKit、是 Safari。这不是打字错误,是二十年兼容史堆出来的。
早年网站用 UA 做嗅探,新浏览器如果不说自己是 Netscape 就被拒之门外,
于是每个新浏览器都往字符串里塞前任的名字,越堆越长。
结果是没人敢改这个字符串,可它又不能用来做特性判断——
一段 120 字节的文本占着每个请求,还天天被拿去做不可靠的判断。
所以 Chrome 从 2022 年开始推 UA 缩减(User-Agent Reduction),
把版本号冻成 `120.0.0.0`,详细信息改由 `Sec-CH-UA` 那组首部按需索取。
也就是说:**这个字段正在变短,别再靠它做任何业务判断**。
长度:Chrome 111、Edge 125、Safari 117、Firefox 80、requests 22、curl 10。
Chrome 扫出来的关键词是「Windows | Chrome | Safari | Gecko」——它有 Safari 和 Gecko,
这两个都是假的。所以只看关键词判断浏览器必然出错。
Edge 只比 Chrome 多了 Edg/ 这一段,所以先判断「有没有 Edg」才能把 Edge 和 Chrome 分开。
这就是为什么 UA 嗅探要写成一长串 if-else,而且永远补不完。
python-requests 和 curl 一望即知,因为它们不伪装。
这也说明一件事:愿意暴露身份的客户端,反而最好识别。
30 秒实验
- 把 Chrome 的 UA 改成
Edg/120结尾,看关键词扫描会不会把它认成 Edge。 - 查一下你正在用的浏览器发出去的 UA,看它有多长。
- 想一想:为什么 Chrome 要把版本号冻成
120.0.0.0?这对指纹追踪有什么影响?
常见误区
- 「UA 里有 Safari 就是 Safari」——Chrome 和 Edge 也都带着这个词。
- 「UA 可以用来做安全判断」——可以伪造,任何基于它的信任都是假的。
- 「UA 会一直这么长」——Chrome 已经在缩减,细节改由
Sec-CH-UA按需索取。
自测三问(点开对答案)
1. Chrome 的 UA 为什么要自称 Mozilla、WebKit、Safari? —— 早年网站靠 UA 嗅探,不说这些名字会被当成不支持的浏览器拒掉。 2. 怎么把 Edge 和 Chrome 从 UA 上区分开? —— 看结尾有没有 `Edg/`。两者主体部分几乎完全一样。 3. 服务端该用 UA 判断客户端能力吗? —— 不该。应该看客户端主动发的能力首部,或者用特性检测。19 个请求首部到此讲完。下一节换方向:服务端能发的 9 个响应首部。
- ☐ (本节完,下一节 6.5.1 Accept-Ranges)