📘 第六章 HTTP首部
📄 一句话概括

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 秒实验

  1. 把 Chrome 的 UA 改成 Edg/120 结尾,看关键词扫描会不会把它认成 Edge。
  2. 查一下你正在用的浏览器发出去的 UA,看它有多长。
  3. 想一想:为什么 Chrome 要把版本号冻成 120.0.0.0?这对指纹追踪有什么影响?

常见误区

  1. 「UA 里有 Safari 就是 Safari」——Chrome 和 Edge 也都带着这个词。
  2. 「UA 可以用来做安全判断」——可以伪造,任何基于它的信任都是假的。
  3. 「UA 会一直这么长」——Chrome 已经在缩减,细节改由 Sec-CH-UA 按需索取。
自测三问(点开对答案) 1. Chrome 的 UA 为什么要自称 Mozilla、WebKit、Safari? —— 早年网站靠 UA 嗅探,不说这些名字会被当成不支持的浏览器拒掉。 2. 怎么把 Edge 和 Chrome 从 UA 上区分开? —— 看结尾有没有 `Edg/`。两者主体部分几乎完全一样。 3. 服务端该用 UA 判断客户端能力吗? —— 不该。应该看客户端主动发的能力首部,或者用特性检测。

19 个请求首部到此讲完。下一节换方向:服务端能发的 9 个响应首部。

相关笔记