浏览器品牌切换:Chrome、Edge、Brave、Opera
说明 User-Agent、Client Hints 与 navigator.userAgentData 为何必须指向同一个浏览器品牌,以及如何设置并检查 Chrome、Edge、Brave 或 Opera 身份。
BotBrowser Team
BotBrowser 能力边界: BotBrowser 在启动时选择品牌后,可以同步 User-Agent、Client Hints、
navigator.userAgentData、品牌顺序、GREASE 和品牌版本。它不会模拟品牌专属界面、功能或扩展行为;运行中的实例不能在不创建新实例的情况下更换品牌;也不能保证网站接受该身份或如何进行检测。
TL;DR
要点(TL;DR): 可靠的品牌切换必须让所有品牌表面一起变化。在启动时设置
--bot-browser-brand,让 Chromium 版本与品牌专属版本保持一致,然后检查 User-Agent、Client Hints 和navigator.userAgentData。一致的品牌只是完整身份的一部分,配置文件、区域设置、平台和网络也必须彼此协调。
要点
内容导航
为什么混杂的浏览器品牌很容易被注意到
浏览器品牌是浏览器对外报告的厂商名称:Google Chrome、Microsoft Edge、Brave、Opera,或者单纯的 Chromium。品牌会同时出现在多个位置,而“这是哪个品牌?”的答案在每个位置都必须一致。如果一个会话在 User-Agent 头中报告 Microsoft Edge,却在 Sec-CH-UA 或 navigator.userAgentData 中仍然只列出 Google Chrome,那么同一个问题就得到了两个不同的答案。
这既关系到隐私,也关系到基本的可用性。网站和分析服务会用品牌信息来划分访客、决定提供哪些功能或提示,并把相似的会话归为一组。当这些信号互相矛盾时,矛盾本身会让浏览器比任何单一品牌更容易被区分,这与注重隐私的配置所要达到的目标正好相反。它还会带来普通的问题,例如页面向 Client Hints 显示为 Chrome 的会话提供了 Edge 专属提示。
有两类读者最常遇到这个问题。需要测试产品在 Edge 或 Brave 用户那里表现的团队,需要浏览器以正在测试的品牌标识自己。出于隐私考虑维护多个相互独立的浏览身份的人,需要每个身份各自保持稳定且内部一致。这两个目标都依赖一致性,而不是某一个单独的值,并且都很容易被局部修改破坏。书签也是可以提前准备的浏览身份的一部分,详见书签注入:预填充浏览器书签。
在更改品牌之前,值得先问三个问题:
- 哪些位置会报告品牌,它们是否会一起变化?
- 品牌专属版本是否属于与身份其余部分相同的 Chromium 主版本?
- 配置文件、网络、区域设置和平台的选择是否仍然描述同一个自洽的浏览器?
承载品牌身份的各个位置
在基于 Chromium 的浏览器中,品牌通过 User-Agent 字符串、Client Hints 请求头、navigator.userAgentData API、品牌列表的顺序与形态,以及附在每个条目上的版本号来表达。下面分别说明。
User-Agent 字符串
User-Agent 请求头和 navigator.userAgent 把品牌作为产品标记放在字符串末尾。所有基于 Chromium 的浏览器为了兼容性都保留 Chrome/ 和 Safari/537.36 标记,因此有差异的是厂商标记:
Chrome: Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36
Edge: Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 Edg/142.0.3595.65
Brave: Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36
Opera: Mozilla/5.0 ... Chrome/142.0.7444.60 Safari/537.36 OPR/<opera 版本>
Brave 在这个字符串里没有自己的厂商标记,所以它的品牌体现在 Client Hints 中。这也是只检查字符串不足以判断浏览器实际报告内容的原因之一。
Client Hints 请求头
Client Hints 是用结构化方式取代解析 User-Agent 字符串的机制。该机制由 RFC 8942 定义,用户代理提示则记录在 MDN 中。一小组低熵提示(包括 Sec-CH-UA)默认随请求发送。更详细的值,例如 Sec-CH-UA-Full-Version-List,只有在服务器通过 Accept-CH 响应头提出请求之后才会发送。Sec-CH-UA 的值列出带主版本号的品牌标记:
- Chrome:
"Chromium";v="142", "Google Chrome";v="142", "Not:A-Brand";v="99" - Edge:
"Chromium";v="142", "Microsoft Edge";v="142", "Not:A-Brand";v="99" - Brave:
"Chromium";v="142", "Brave";v="142", "Not:A-Brand";v="99"
navigator.userAgentData API
同一信息在 JavaScript 中的形式是 navigator.userAgentData。它的 brands 属性对应低熵的 Sec-CH-UA 列表,getHighEntropyValues() 返回一个 Promise,其中包含 fullVersionList、platform 和 platformVersion 等详细字段。请求头怎么说,这个 API 也应当怎么说:
navigator.userAgentData.brands;
// Chrome: [{brand: "Chromium", version: "142"}, {brand: "Google Chrome", version: "142"}, ...]
// Edge: [{brand: "Chromium", version: "142"}, {brand: "Microsoft Edge", version: "142"}, ...]
由于高熵值只在被请求时才会返回,不一致可能一直隐藏到某个网站来索取它们为止。首次加载时看起来正确的配置,之后仍可能自相矛盾,所以下面的检查同时覆盖默认值和详细值。 关于服务器如何把这些请求头当作身份信号,请参阅 Client Hints 指纹识别:HTTP 头部与身份暴露,其中介绍了每个提示所透露的内容。
标记顺序、GREASE 条目与版本节奏
Chromium 会在品牌列表中加入一个刻意没有意义的 “GREASE” 条目,防止服务器依赖固定的列表格式。它的文本和位置各不相同。根据 BotBrowser 文档,品牌数组包含正确的 GREASE 标记,因此 Sec-CH-UA 中的列表与 navigator.userAgentData.brands 中的列表应当彼此一致,顺序也相同。
版本节奏是另一个容易出错的细节。Chrome、Edge 和 Opera 各自按自己的节奏发布版本。Chrome 和 Edge 共享 Chromium 主版本,但完整版本号不同:Chrome 142 可能是 142.0.7444.60,而对应的 Edge 构建是 142.0.3595.65。Opera 的 OPR/ 值遵循 Opera 自己的编号,而不是 Chromium 的编号。对于 Chrome 和 Edge,如果某个品牌条目的完整版本不属于身份其余部分所用的 Chromium 主版本,即使每个标记名称都正确,也是一种不一致。
为什么只替换 User-Agent 不够
自动化框架让修改字符串变得很容易。Playwright 在创建上下文时接受 userAgent 选项,Puppeteer 提供 page.setUserAgent():
// Playwright
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 ... Edg/142.0.3595.65',
});
// Puppeteer
await page.setUserAgent('Mozilla/5.0 ... Edg/142.0.3595.65');
只替换字符串会改变请求头和 navigator.userAgent。如果没有单独提供 Client Hints 元数据,其他位置仍会报告原来的品牌:
Sec-CH-UA仍然列出原品牌的标记。navigator.userAgentData.brands仍然返回原来的列表。Sec-CH-UA-Full-Version-List仍然带着原来的版本号。getHighEntropyValues()返回的值仍然描述原来的浏览器。
结果是一个身份在某处说自己是 Edge,在另一处却说是 Chrome。手动提供元数据可以弥补一部分差距,但会把工作转嫁给你:标记顺序、GREASE 条目、完整版本,以及每个页面、worker 和请求,都得手动保持同步,而手写的列表很容易在细节上出错。 关于请求头、Client Hints 与 navigator.userAgentData 应如何保持一致,请参阅 User-Agent 与 Client Hints:保持身份一致,其中介绍了如何设置和检查自定义身份。
手动维护的覆盖还会很快过时。当 Chromium 主版本更新时,脚本里每一个写死的字符串和版本号都会失效,下一次更新就可能让 User-Agent 停在一个版本,而 Client Hints 停在另一个版本。由浏览器在所有位置统一应用的单一品牌设置可以省去这类维护。从完整 User-Agent 字符串转向客户端提示的过程,可参阅用户代理精简与客户端提示迁移实践。
任何一次只改写一个位置的方法都有相同形态的问题。扩展里的请求头规则、框架里的字符串覆盖、只修改某个属性的脚本,各自只覆盖自己的位置,没有任何机制保证这些位置仍然一致。可靠的做法是在一开始只选择一次品牌,让所有位置都由这个选择派生。
使用 BotBrowser 在启动时设置品牌
BotBrowser 的 --bot-browser-brand 参数(ENT Tier2;webview 取值需要 ENT Tier3)在浏览器启动时选择品牌。根据 BotBrowser 文档,设置该参数会同时调整 User-Agent 字符串、navigator.userAgentData 品牌列表、Client Hints 请求头及相关值,并且结果在主线程、Workers 和 HTTP 请求头中保持一致。
| 位置 | 是否跟随所选品牌 |
|---|---|
User-Agent 请求头 | 是 |
navigator.userAgent | 是 |
Sec-CH-UA 请求头 | 是 |
Sec-CH-UA-Full-Version-List | 是 |
navigator.userAgentData.brands | 是 |
getHighEntropyValues() | 是 |
| GREASE 条目 | 是 |
支持的品牌取值
| 品牌 | 参数取值 | 说明 |
|---|---|---|
| Chrome | chrome | 默认的基于 Chromium 的身份 |
| Edge | edge | 带 Edg/ 标记的 Microsoft Edge |
| Brave | brave | Brave 浏览器身份 |
| Opera | opera | 带 OPR/ 标记的 Opera |
| Chromium | chromium | 不带厂商品牌的纯 Chromium 身份 |
| WebView | webview | Android WebView(ENT Tier3) |
让品牌版本与 Chromium 版本匹配
切换到 Edge 或 Opera 时,厂商的完整版本必须与配置文件中的 Chromium 版本相符。有两个参数负责这件事,文档建议两者使用相同的主版本号。Opera 自己的 OPR/ 号码仍遵循 Opera 自己的编号:
--bot-brand-full-version设置品牌专属的完整版本,例如 Edge 的142.0.3595.65。--bot-ua-full-version设置 Chromium 的完整版本,例如142.0.7444.60。
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=edge \
--bot-ua-full-version=142.0.7444.60 \
--bot-brand-full-version=142.0.3595.65
Edge、Brave 和 Opera 的启动示例
在命令行中,每个品牌只需在配置文件旁边加一个参数。请搭配与所呈现身份相符的时区、区域设置和网络路由:
# Edge 身份,美国区域设置
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=edge \
--bot-timezone=America/New_York \
--bot-locale=en-US
# Brave 身份,英国区域设置
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=brave \
--bot-timezone=Europe/London \
--bot-locale=en-GB
# Opera 身份
chrome --bot-profile="path/to/profile.enc" \
--bot-browser-brand=opera
Playwright 和 Puppeteer 通过 args 选项传入相同的参数。文档指出,品牌参数应放在 args 中,而不是框架自带的选项里,这是品牌没有变化的常见原因:
const { chromium } = require('playwright-core');
const browser = await chromium.launch({
executablePath: 'path/to/botbrowser/chrome',
headless: false,
args: ['--bot-profile=path/to/profile.enc', '--bot-browser-brand=edge', '--bot-brand-full-version=142.0.3595.65'],
});
一个实际例子可以说明预期结果。假设配置文件使用 Chromium 142,而你想要 Edge 身份。你用 --bot-browser-brand=edge、--bot-ua-full-version=142.0.7444.60 和 --bot-brand-full-version=142.0.3595.65 启动。此时 User-Agent 字符串以 Edg/142.0.3595.65 结尾,Sec-CH-UA 在主版本 142 下同时列出 Microsoft Edge 和 Chromium,fullVersionList 则把 Chromium 和 Edge 的完整版本作为各自独立的条目。如果这三项中有任何一项与其他不一致,说明某个版本参数缺失或自相矛盾。
WebView 身份
Android WebView 身份(ENT Tier3)需要的不只是品牌。请把 webview 品牌与 User-Agent 模板,以及来自 Android 配置文件的平台参数结合使用。{platform-version} 和 {model} 这类占位符会根据对应参数填充,BotBrowser 会生成相匹配的 navigator.userAgentData 和 Client Hints 值:
chrome --bot-profile="path/to/android-profile.enc" \
--bot-browser-brand=webview \
--user-agent="Mozilla/5.0 (Linux; Android {platform-version}; {model} Build/TP1A.220624.021; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/{ua-full-version} Mobile Safari/537.36" \
--bot-platform=Android \
--bot-platform-version=13 \
--bot-model=SM-G991B \
--bot-mobile=true
让多个品牌彼此隔离
当同一台机器服务多个身份时,请为每个身份提供独立的配置文件和独立的用户数据目录,并按身份分别设置品牌,避免任何内容在它们之间串用:
chrome --bot-profile="profiles/chrome-user.enc" \
--bot-browser-brand=chrome \
--user-data-dir="/tmp/chrome-session"
chrome --bot-profile="profiles/edge-user.enc" \
--bot-browser-brand=edge \
--user-data-dir="/tmp/edge-session"
文档还描述了在创建浏览器上下文时为每个上下文设置不同品牌的做法(ENT Tier3)。在这两种情况下,品牌都是在创建实例或上下文时确定的,所以要更改品牌就需要启动新的实例或上下文。
检查各处是否只显示同一个品牌
启动后,可以直接在浏览器中验证结果,不需要额外工具。在任意页面打开开发者工具,按下面的清单逐项检查:
User-Agent字符串包含正确的厂商标记(Edge 为Edg/,Opera 为OPR/)。Sec-CH-UA请求头列出预期的品牌标记。navigator.userAgentData.brands与请求头一致。fullVersionList在 Chromium 版本旁边显示品牌专属版本,对于 Chrome 和 Edge,两者主版本号相同。Opera 的品牌版本遵循 Opera 自己的编号。- 除了所有基于 Chromium 的品牌都会包含的 Chromium 基础标记之外,没有出现其他品牌名称。
要查看脚本可见的值,在控制台中粘贴三行代码:
navigator.userAgent;
navigator.userAgentData.brands;
await navigator.userAgentData.getHighEntropyValues(['fullVersionList', 'platform', 'platformVersion']);
要查看请求头,请打开网络面板,选择一个顶层 HTTPS 请求,阅读其请求头。Sec-CH-UA-Full-Version-List 这类详细请求头只有在网站通过 Accept-CH 提出请求后才会出现,所以首次请求中没有它们是正常的,并不表示不一致。如果你的页面会启动 worker,请在 worker 的上下文中执行同样的控制台代码,确认它报告的品牌与页面相同。
清单全部通过只说明浏览器自身是一致的,并不说明某个具体网站会如何对待这个会话,因为网站会综合许多输入,而且会随时间调整自己的规则。请用这份清单确认自己的配置,并让身份的其余部分,例如配置文件、网络路由、区域设置、渲染、显示、语言和请求元数据,同样保持一致。
当结果看起来不对时,症状通常能指向原因:
| 症状 | 可能原因 | 需要调整的内容 |
|---|---|---|
UA 字符串显示 Edg/,但品牌列表只有 Google Chrome | 只修改了字符串 | 在启动时设置品牌,而不是覆盖字符串 |
fullVersionList 中的 Edge 版本看起来像 Chrome 的 | 没有设置品牌专属版本 | 添加 --bot-brand-full-version |
| Edge 的 UA 字符串与品牌列表中的 Chromium 主版本不同 | 两个版本参数的主版本不一致 | 让 --bot-ua-full-version 和 --bot-brand-full-version 与配置文件对齐 |
| 品牌始终没有变化 | 参数是作为框架选项传入的 | 放进 args 中传入 |
| 品牌与配置文件中的不同 | 命令行参数的优先级高于配置文件 | 移除该参数,或更新配置文件 |
每次更改配置文件、Chromium 版本或品牌参数之后都要重复检查,因为其中任何一项都可能只移动一个位置而不移动其他位置。
基于 Chromium 公开行为的可复现实测
下面的小型夹具可以展示边界,但不声称运行过 BotBrowser。页面记录 navigator.userAgent、低熵 brands 数组和 fullVersionList;服务器记录 User-Agent 与 Sec-CH-UA,再用 Accept-CH 请求详细提示。按照 Chromium 的公开模型,第一个请求可能带有低熵品牌,却没有 Sec-CH-UA-Full-Version-List;第二个请求才可能带上详细列表。只改字符串的覆盖通常不会自动同步结构化元数据。
const observation = {
ua: navigator.userAgent,
brands: navigator.userAgentData?.brands ?? null,
high: navigator.userAgentData ? await navigator.userAgentData.getHighEntropyValues(['fullVersionList']) : null,
};
console.table(observation);
| 观测点 | 预期结果 | 排障价值 |
|---|---|---|
| 初始请求 | User-Agent 与低熵 Sec-CH-UA | 在请求详细提示前发现只改 UA 的覆盖 |
Accept-CH 之后的请求 | 可能出现 Sec-CH-UA-Full-Version-List | 区分尚未请求详细提示与真实版本不一致 |
| 页面脚本 | brands、fullVersionList 与请求头一致 | 发现只改请求头而没有改 JavaScript 表面的规则 |
这是基于 MDN NavigatorUAData API 和 Client Hints 规范 的行为观测,不是对某个 BotBrowser 配置文件的声明。普通 Chromium 构建即可复现,三行中都应出现同一个品牌和同一个 Chromium 主版本。
结论
请把品牌看作完整身份的一部分,而不是一个孤立的开关:
- 保持版本对齐。使用
--bot-brand-full-version,让厂商版本属于配置文件中的 Chromium 主版本。 - 从合适的配置文件出发。取自真实 Edge 浏览器的配置文件,配合
--bot-browser-brand=edge,能得到最一致的结果。 - 让周边设置相互匹配。品牌、时区、区域设置、语言和网络路由应描述同一类用户。
- 在一个身份的整个生命周期内保持同一个品牌。在同一网站的不同访问之间更换品牌,会留下前后不一致的历史。
- 在浏览器或配置文件更新后,重新执行检查清单。
这个参数并非总是必需。已加载的配置文件本身带有自洽的品牌,所以不设置 --bot-browser-brand 是保持一致的最简单方式。只有在有理由呈现另一个品牌时才使用该参数,例如复现只出现在 Edge 上的支持问题,或者给第二个身份分配它自己的品牌,并且随后也要确认版本参数。每增加一个覆盖项,就多一个必须与其余部分保持同步的值,所以只修改任务真正需要的内容。
下面几个实际问题经常出现:
- 切换品牌会改变浏览器的功能吗? 不会。它更新的是身份值,而不是浏览器界面。Brave 的屏蔽功能或 Edge 的侧边栏等品牌专属功能不会被模拟。
- 任何品牌都能搭配任何配置文件吗? 对于非 WebView 品牌,可以使用任意兼容的配置文件;该参数会覆盖该配置文件中的品牌,命令行参数的优先级最高。WebView 仍须满足上面所示的 Android 平台、型号和移动设备参数。
- 不使用该参数会怎样? 使用已加载配置文件中保存的品牌;如果配置文件没有指定,则默认是 Chrome。
- 品牌会影响扩展吗? 文档没有描述对扩展的影响,因此更换品牌后请测试你依赖的扩展。
- 浏览器运行时可以更改品牌吗? 不可以。品牌在启动时或创建上下文时确定,要换品牌就需要新的实例。
- WebView 有什么不同? 有。它还需要上面展示的平台、型号和移动设备参数。
BotBrowser 支持在启动时通过 --bot-browser-brand(chrome、edge、brave、opera、chromium 或 webview)切换浏览器品牌,并通过 --bot-brand-full-version 设置品牌专属版本,让 User-Agent、navigator.userAgentData 品牌和 Client Hints 头部同步变化而不是相互脱节。BotBrowser 不会模拟品牌专属的界面或功能;它无法更改正在运行的实例或上下文的品牌,因为品牌在创建实例或上下文时就已确定;也无法保证网站如何对待最终得到的身份。
有关 User-Agent 和 Client Hints 详情,请按本文的检查步骤操作,并让地理设置与所选品牌保持一致。
来源
本文属于指南: 浏览器身份表面:一致性实用总览