了解 User-Agent Client Hints(UA-CH)的工作原理:低熵与高熵层级、Accept-CH 协商机制,以及对浏览器指纹识别的影响。
User-Agent 字符串自 1990 年代初便是 Web 的组成部分——这个单一请求头会一次性声明你的浏览器、版本、引擎、操作系统和设备类型。几十年来,它愈发臃肿:承载着"Mozilla/5.0"这类早已作古的历史残留令牌,以及足以充当被动指纹信号的大量平台细节。User-Agent Client Hints(UA-CH)是 Chromium 的结构化替代方案。它不再预先广播所有信息,而采用双层披露模型:每次请求默认发送一小组低熵提示,而更丰富的高熵层级只在服务器明确请求时才释放。
核心要点
- Chromium 浏览器默认在每个 HTTPS 请求中发送三项低熵提示(品牌列表、移动设备标志、平台名称)——无需服务器主动申请。
- 架构信息、完整版本号、操作系统版本、设备型号等高熵细节,需要服务器先发送
Accept-CH响应头才会提供。 - JavaScript 对应接口为
navigator.userAgentData——这是 Chromium 专属 API,Firefox 和 Safari 返回undefined。 - Chromium 正在逐步将旧版
User-Agent字符串冻结为低细节存根,同时将 UA-CH 扩展为主要的身份标识通道。 - 对于指纹识别和伪装检测而言,
navigator.userAgentData的存在本身就能可靠地识别 Chromium——而授权后的高熵值所暴露的浏览器身份细节,比旧版 UA 字符串更为具体。
为何旧版 User-Agent 需要被替代
最初的 UA 字符串是为浏览器嗅探而设计的——服务器读取它,判断使用的是哪种渲染引擎,然后发送相应内容。它从未以隐私为出发点。典型的 Chromium UA 字符串如下:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
这个单一请求头会被动泄露操作系统名称和版本、CPU 架构、平台类型、浏览器品牌以及粗略的版本号——在每次请求中,不需要任何服务器主动询问。它还成了兼容性陷阱:网站开始根据特定子字符串做分支处理,导致浏览器无法修改格式,否则就会打破这些网站的适配逻辑。这既是维护问题,也是隐私问题。UA-CH 两者兼顾:它将旧版字符串冻结为存根,同时提供清晰的版本化 API,让服务器只申请它真正需要的内容。
两个层级:低熵与高熵提示
UA-CH 模型根据信息对缩小用户身份范围的程度,将浏览器信息分为两个层级。
低熵提示(默认发送)
Chromium 在每个跨域 HTTPS 请求中附加三个 Sec-CH-UA-* 请求头,无需服务器提示:
| 请求头 | 示例值 | 披露内容 |
|---|---|---|
Sec-CH-UA | "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99" | 浏览器品牌列表及主版本号 |
Sec-CH-UA-Mobile | ?0 | 是否为移动设备(?1)或非移动设备(?0) |
Sec-CH-UA-Platform | "Windows" | 仅操作系统的高层次名称 |
这些属于低熵信息,因为大量用户共享相同的值:知道某人用的是 Windows 版 Chrome 124,可以对应数百万用户,而不是寥寥几人。
高熵提示(需要 Accept-CH)
分辨率更高的细节——那些可能精确定位特定设备版本的信息——默认关闭,服务器必须明确申请:
| 请求头 | 示例值 | 披露内容 |
|---|---|---|
Sec-CH-UA-Arch | "x86" | CPU 架构 |
Sec-CH-UA-Bitness | "64" | 32 位或 64 位平台 |
Sec-CH-UA-Full-Version-List | "Google Chrome";v="124.0.6367.60" | 每个品牌的完整版本字符串 |
Sec-CH-UA-Model | "Pixel 7" | 设备型号(主要对移动端有意义) |
Sec-CH-UA-Platform-Version | "10.0.0" | 操作系统版本 |
Accept-CH 协商机制
申请高熵提示的协议流程很直接。首次访问时,服务器返回一个 Accept-CH 响应头,列出所需的提示名称:
HTTP/1.1 200 OK
Accept-CH: Sec-CH-UA-Full-Version-List, Sec-CH-UA-Arch, Sec-CH-UA-Platform-Version
在后续请求(以及页面刷新)中,浏览器会包含这些提示:
GET /page HTTP/1.1
Sec-CH-UA: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"
Sec-CH-UA-Mobile: ?0
Sec-CH-UA-Platform: "Windows"
Sec-CH-UA-Full-Version-List: "Chromium";v="124.0.6367.60", "Google Chrome";v="124.0.6367.60", "Not-A.Brand";v="99.0.0.0"
Sec-CH-UA-Arch: "x86"
Sec-CH-UA-Platform-Version: "10.0.0"
首次页面加载故意保持信息稀疏:如果没有事先的明确申请,服务器仅能看到三项低熵默认值。
JavaScript API:navigator.userAgentData
相同的数据也可通过 navigator.userAgentData 在客户端获取。该 API 仅存在于 Chromium 中——Firefox 和 Safari 返回 undefined——因此它的存在本身就是一个可靠的引擎识别信号。
低熵数据是同步的:
const uaData = navigator.userAgentData;
console.log(uaData.brands); // [{brand: "Chromium", version: "124"}, ...]
console.log(uaData.mobile); // false
console.log(uaData.platform); // "Windows"
高熵数据是异步的,需要显式请求——与 HTTP Accept-CH 模型相对应:
navigator.userAgentData
.getHighEntropyValues(["architecture", "platformVersion", "fullVersionList"])
.then(ua => {
console.log(ua.architecture); // "x86"
console.log(ua.platformVersion); // "10.0.0"
console.log(ua.fullVersionList); // [{brand: "Google Chrome", version: "124.0.6367.60"}, ...]
});
调用 getHighEntropyValues() 不会触发用户可见的权限提示。它为网站提供了一种可追溯的显式机制来获取详细的身份信息,而非静默读取广播字符串。跨域 iframe 可通过 Permissions-Policy 被阻止获取高熵提示。
UA-CH 与旧版 User-Agent 的对比
| 属性 | 旧版 User-Agent | User-Agent Client Hints |
|---|---|---|
| 浏览器支持 | 所有浏览器 | 仅 Chromium |
| 披露模型 | 所有内容被动广播 | 低熵默认 + 高熵按需申请 |
| 格式 | 无结构自由文本字符串 | 结构化键值对 |
| 可伪造性 | 轻而易举(修改一个字符串) | 逐字段伪造,难以保持一致 |
| 隐私设计意图 | 无(历史遗留) | 内置渐进式披露设计 |
| 发展趋势 | 逐步冻结为存根 | 持续扩展;Chromium 主要身份通道 |
Chromium 一直在逐步减少旧版 UA 字符串中的信息量。自 Chrome 101 起,次要操作系统版本已冻结为 0.0.0,UA 字符串中的浏览器次要版本也经过了简化。发展方向是让其变成一个仅传达"这是 Chromium 浏览器"含义的存根——所有具体信息都迁移到 UA-CH。
指纹识别影响
UA-CH 从两个方向改变了指纹识别格局。
navigator.userAgentData 的存在是可靠的引擎检测器。 Firefox 和 Safari 不暴露此 API。通过检查 navigator.userAgentData !== undefined,页面可以立即识别 Chromium——比解析可随意伪造的 UA 字符串更为可靠。如果伪装的 UA 声称是 Firefox,但 navigator.userAgentData 却存在并报告 Chromium 品牌,这一矛盾立刻显而易见。这是检测 user-agent 伪装中的核心技术手段之一。
高熵提示可能比旧版 UA 字符串更具体。 已冻结的旧版 UA 不再暴露完整的次要浏览器版本;Sec-CH-UA-Full-Version-List 则会暴露。结合 Sec-CH-UA-Arch 和 Sec-CH-UA-Platform-Version,请求完整集合的服务器所获得的平台指纹,比被动广播时代更为丰富——但这需要浏览器的配合,且仅对明确申请的服务器有效。
冻结 UA 字符串并不等于改善隐私。 减少 UA 字符串只是移除了一个被动信号,对构成浏览器指纹的其他众多信号毫无影响:canvas 渲染输出、WebGL 渲染器字符串、屏幕参数、音频处理、已安装字体。UA-CH 改善的是服务器端的信息治理,并未缩小整体指纹暴露面。
UA-CH 由服务器控制,而非用户控制。 目前没有浏览器 UI 允许用户拒绝第一方 JavaScript 发起的特定 getHighEntropyValues() 调用。Brave 的指纹随机化模式确实会覆盖 UA-CH 值,其他基于 Chromium 的浏览器则没有此功能。需要限制信息披露的用户,只能依赖浏览器的反指纹模式。
自行测试
打开浏览器开发者工具的控制台,运行 navigator.userAgentData 查看低熵值,然后运行:
navigator.userAgentData?.getHighEntropyValues(
["architecture", "model", "platformVersion", "fullVersionList"]
)
在 Firefox 或 Safari 中,该 API 返回 undefined;在 Chromium 中,它会解析出你的实际平台详情。BrowserInsight 的指纹检测工具会连同其余指纹信号一并展示你的完整 UA-CH 数据,让你看清所有身份信号是如何组合在一起的。
常见问题
UA-CH 会完全替代 User-Agent 字符串吗?
暂时不会,对非 Chromium 浏览器也不会。Firefox 和 Safari 仍只发送传统的 User-Agent 请求头。对于 Chromium,UA-CH 是首选通道,旧版 UA 正在冻结中,但两者在当前版本中共存。需要广泛兼容性的网站仍会将 User-Agent 作为备用方案读取。
我能伪造 UA-CH 的值吗?
无法通过普通浏览器设置实现。Chrome 的开发者工具设备模拟会覆盖 UA 字符串,但不会覆盖 navigator.userAgentData,这也是设备模拟容易被检测到的原因。Brave 的指纹随机化模式确实提供 UA-CH 伪造功能,但在 HTTP 请求头和 JavaScript API 两端保持一致,需要浏览器层面的协调修补。
UA-CH 能在 HTTP 下工作吗?
不能。Accept-CH 协商和 Sec-CH-UA-* 请求头仅适用于 HTTPS(以及 localhost),普通 HTTP 连接不会发送 UA-CH 请求头。
Firefox 支持 UA-CH 吗?
Firefox 不实现 UA-CH。Mozilla 团队曾表达顾虑,认为当高熵值被常规请求时,该 API 可能让指纹识别变得更容易。Firefox 继续使用旧版 User-Agent 请求头。
结论
User-Agent Client Hints 代表了一次深思熟虑的架构转变:从一个无论是否有人询问都会泄露平台细节的单一被动广播,转向一个低熵事实默认发送、高熵细节需要申请才释放的分层系统。对于开发者而言,结构化 API 比自由文本 UA 字符串更易于解析和版本管理。对于指纹识别和检测工作而言,它引入了一个明确的 Chromium 与非 Chromium 区分信号,同时为有需要的服务器提供了更丰富的显式身份标识通道。冻结旧版 UA 字符串减少了一个被动向量;取而代之的,是浏览器与服务器之间的主动协商。
延伸阅读:


