浏览器代理改变的是页面请求的来源。DNS 查询、WebRTC 连接和浏览器时区仍可能指向本机自己的网络。本文介绍如何用 ToDetect 的免费工具逐项检查,通过和失败分别是什么样子,以及哪些修复在我们的测试中确实有效。
示例来自 ipvolt 做过的一次真实测试:
- 时间:2026 年 10 月 23 日,16:45 至 16:57 UTC。
- 机器:一台运行 macOS 15.7.4 的 Mac。
- 浏览器:Chromium 153 和 Firefox,由 Playwright 1.63.0 驱动,分别在 headless 模式和普通窗口中运行。
- 代理:一个 Vodafone Germany 网络上的 4G 移动代理,我们用它做上线前测试,配置为带用户名和密码的 HTTP 代理。德国移动网络的背景见德国代理。
- 本机自身的连接:位于爱沙尼亚塔林的商业 VPN 出口,属于数据中心地址。
- 遮盖:截图中的 IP 地址已遮盖,结果文件中的 IP 也已删除,文件列出了每次运行的计数和评分。
1. 先在不使用代理时取得基线
先在关闭代理的情况下把每项测试跑一遍,了解你自己的连接是什么样子。我们在 ToDetect 的 IP Quality Check(IP 质量检测)上的基线是:位置 Tallinn,IP 类型 Datacenter,ASN 组织标记为 HOSTING,IP Quality Score 为 68%。DNS Leak Test(DNS 泄露测试)只显示一个解析器,即 VPN 的解析器,位于爱沙尼亚。
2. 检查网站看到的 IP
打开代理并重新加载 IP Quality Check。对照代理应有的情况检查四个字段:位置、ISP 和 ASN、IP 类型以及时区。
通过移动代理,页面显示 Berlin、ISP D2VODAFONE、ASN 3209(Vodafone GmbH)、IP 类型 Mobile、IP Abuse 为“No”、时区 Europe/Berlin、语言 German。
通过 4G 测试线路的 ToDetect IP Quality Check,2026 年 10 月 23 日。IP 地址和前缀已遮盖。
评分由页面列出的扣分项构成,可以展开查看评分变化的原因:
| 连接 | IP 类型 | 评分 | 显示的扣分项 |
|---|---|---|---|
| 本机自身的连接(VPN,爱沙尼亚) | Datacenter | 68% | IP Usage Frequency -3%, VPS/IDC Provider -25%, Occasional ASN Organization Change -4% |
| 4G 移动代理(Vodafone Germany) | Mobile | 94% | IP Usage Frequency -3%, Regional ISP -3% |
差距主要来自 VPS/IDC Provider 这一项扣分。通过的标准是位置、ISP 和 IP 类型与你配置的代理一致;第 5 步再比较时区与浏览器。如果仍然看到你自己的 ISP 或城市,说明浏览器没有使用代理,在修好之前其他测试意义不大。评分反映的是 ToDetect 自己的数据库,把它当作参考信号之一即可。
3. 检查 DNS 在哪里解析
在代理开启时运行 DNS Leak Test。它会列出为你的浏览器应答查询的 DNS 解析器。通过的结果是:解析器属于代理所在的网络,或者是你有意选择的解析器。失败的结果是:出现你自己 ISP 或你自己网络的解析器。
通过代理时,解析器是 139.7.74.9 和 139.7.74.10,均显示为“Vodafone Germany, Germany”。基线中的爱沙尼亚解析器不再出现。
两个解析器都属于 Vodafone Germany。顶部的客户端 IP 已遮盖。
对 Chromium 中的 HTTP 代理来说这是预期结果。Chromium 文档写道“name resolution is always deferred to the proxy”(域名解析总是交给代理完成):主机名随 CONNECT 请求一起发送。DNS 泄露可能出现在 SOCKS4 上,或出现在连接前先在本地解析域名的 SOCKS5 客户端上,例如使用 socks5:// 而不是 socks5h:// 的 curl。这种情况见 SOCKS5 与 SOCKS5h。
4. 检查 WebRTC
打开 WebRTC Leak Test(WebRTC 泄露测试)。它会询问 17 个 STUN 服务器,你的浏览器看起来来自哪个公网地址。通过的结果是:每个服务器都显示“Not detected”或代理的地址。失败的结果是:出现红色的“WebRTC IP Exposure Detected”提示,并且 Public IP 字段中出现你自己的地址。
在默认设置下,两个浏览器都没有通过:
- Chromium 默认设置:17 个 STUN 服务器中有 13 个返回了本机自身的地址(三次运行中,IPv4 为 11 或 12 个,IPv6 为 1 或 2 个)。其余 4 个显示“Not detected”。
- Firefox 默认设置:17 个中有 10 个返回了本机自身的 IPv4 地址,4 个显示“Not detected”。其余 3 个显示 0.0.0.0,既没有显示本机的 IP,也没有显示代理的 IP;我们没有查明原因。
页面根据代理的 IP 把连接定位在柏林,但 STUN 服务器返回了本机自身的地址(已遮盖):本次运行为 17 个中的 13 个。裁剪后的截图只显示其中三个。
原因在于传输方式。WebRTC 通过 UDP 发送 STUN 请求。标准 HTTP 代理只转发 TCP 连接,因此默认情况下这些请求会经由本机自身的连接发出,STUN 服务器看到的就是这个公网 IP。RFC 8828 描述了这种情况:如果允许直接访问互联网,“WebRTC's STUN checks will bypass the proxy and reveal the public IP address of the client”(WebRTC 的 STUN 检查会绕过代理并暴露客户端的公网 IP 地址)。
我们测试过的修复
| 浏览器和设置 | 显示本机自身地址的 STUN 服务器 | 结果 |
|---|---|---|
| Chromium 默认设置 | 17 个中 13 个(三次运行) | 泄露 |
Chromium headless,--force-webrtc-ip-handling-policy=disable_non_proxied_udp | 17 个中 0 个(一次运行) | 通过 |
| Chromium 普通窗口,同一参数 | 17 个中 13 个(两次运行) | 泄露 |
Chromium 普通窗口,配置文件首选项 webrtc.ip_handling_policy = disable_non_proxied_udp | 17 个中 0 个(两次运行) | 通过 |
| Firefox 默认设置 | 17 个中 10 个(一次运行) | 泄露 |
Firefox,media.peerconnection.ice.proxy_only = true | 17 个中 0 个(一次运行) | 通过 |
计数同时包括 IPv4 和 IPv6 地址。
命令行参数对 headless Chromium 有效,对普通窗口无效。Chromium 源码中,这个开关由 headless shell 读取,而普通 Chrome 读取的是配置文件首选项 webrtc.ip_handling_policy。我们没有找到说明该参数在普通窗口中会被忽略的文档,因此请把这看作实测结果,而不是有文档依据的规则。
我们实测的 Chromium 修复方法,是在启动前把该首选项写入配置文件的 Default/Preferences 文件。在 Playwright 中这需要使用持久化上下文:
import { chromium } from 'playwright';
import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
const dir = mkdtempSync(join(tmpdir(), 'leak-check-'));
mkdirSync(join(dir, 'Default'));
writeFileSync(
join(dir, 'Default', 'Preferences'),
JSON.stringify({ webrtc: { ip_handling_policy: 'disable_non_proxied_udp' } }),
);
const context = await chromium.launchPersistentContext(dir, {
headless: false,
proxy: {
server: process.env.PROXY_SERVER,
username: process.env.PROXY_USERNAME,
password: process.env.PROXY_PASSWORD,
},
});PROXY_SERVER 保存代理 URL,包括协议和端口。客户端的其余配置见 Playwright 代理设置。
在 Chrome 中设置同一首选项的另外两种方法,我们没有测试:
- 把企业策略 WebRtcIPHandling 设为
disable_non_proxied_udp。这样 Chrome 只有在所配置的代理支持 UDP 时才使用 UDP,否则通过代理使用 TCP。策略页面给出了 Windows 注册表位置以及 macOS 和 Linux 上的首选项名称。 - 安装 Google 的 WebRTC Network Limiter 扩展。其商店页面说明,“禁用非代理 UDP”默认未启用,需要在 Options 页面中开启。
根据 Chromium 的策略定义和 chrome.privacy 文档,这两种方法控制的都是我们设置的那个首选项。采用其中任何一种后,请重新运行 WebRTC 测试。
在 Firefox 中,打开 about:config,把 media.peerconnection.ice.proxy_only 设为 true,让 WebRTC 流量只经过所配置的代理。在 Playwright 中,把它作为 firefoxUserPrefs: { 'media.peerconnection.ice.proxy_only': true } 传给 firefox.launch()。把 media.peerconnection.enabled 设为 false 会完全关闭 WebRTC。我们只测试了第一项设置,且没有加上第 5 步的时区选项。
Chromium 普通窗口加配置文件首选项:没有暴露提示,17 个服务器全部为“Not detected”(截图显示三个)。
5. 检查时区
打开 Timezone Detection(时区检测)页面。它会把根据 IP 推断的时区与浏览器自身的时区 ID 进行比较。如果你要检查网站对代理所在地访客的表现,例如本地化页面或广告投放,两者应当一致。
代理开启、浏览器保持本机设置时,基于 IP 的时区是 Europe/Berlin,而浏览器的 IANA 时区 ID 是 Europe/Vilnius。也就是说,时区 ID 不一致。代理改变的是网络路径,而不是浏览器的时区设置。在我们的运行中,ToDetect 显示两者的 UTC 偏移相同,只有时区 ID 不同。
基于 IP 的时区跟随代理,浏览器的时区 ID 仍停留在本机设置上。
随后在 Chromium 中,我们在第 4 步的 launchPersistentContext() 选项里加入了 timezoneId: 'Europe/Berlin' 和 locale: 'de-DE'。“修复后”的截图使用的就是这套组合设置。之后两个时区都显示为 Europe/Berlin。
修改后:浏览器的 IANA 时区 ID 为 Europe/Berlin,区域设置为 de-DE。
在没有自动化的普通浏览器中,时区来自操作系统:修改系统时区,并在浏览器设置中设定语言。我们没有测试这种方法,因此修改后请再检查一次页面。
6. 最后再看指纹概览
ToDetect 首页把上述检查汇总成“Browser Fingerprint Consistency”(浏览器指纹一致性)评分,ToDetect 表示该评分“does not represent a definitive pass or fail result”(并不代表最终的通过或失败结果)。在使用持久化配置文件的 Chromium 普通窗口中,按页面显示,默认设置下为 65%,完成 WebRTC 和时区修改后为 87%。Timezone Anomaly 和 WebRTC Alert 扣分项消失了。剩下的 WebDriver 和 ClientRect 扣分来自自动化测试浏览器;Language Anomaly 也仍然存在,我们没有查明是什么触发了它。
Bot Detection(机器人检测)页面在每次运行中都标记了 WebDriver 和 Dev Tool 异常,在 headless 运行中还标记了 Headless Chrome 异常,这对自动化浏览器来说属于预期情况。类似无痕模式的上下文和 headless 模式会额外带来指纹扣分,因此做 QA、广告验证或地理位置检查时,请在你实际工作要用的浏览器配置文件中测试。
我们没有测试的内容
我们没有测试浏览器中的 SOCKS5(Chromium 不支持对 SOCKS5 代理使用用户名和密码登录)、Safari、Edge、移动浏览器,也没有测试会话期间的 IP 轮换。这些结果只描述一条线路、一台机器和一天的情况。
检查清单
- 在关闭代理时记录基线:IP、ISP、IP 类型和 DNS 解析器。
- 开启代理后,确认 IP 检查显示的是代理的位置、ISP 和 IP 类型,而不是你自己的。
- 确认 DNS Leak Test 只列出代理网络的解析器或你选择的解析器。
- 运行 WebRTC Leak Test。如果出现你自己的地址,把 Chrome 的
webrtc.ip_handling_policy首选项设为disable_non_proxied_udp,或把 Firefox 的media.peerconnection.ice.proxy_only设为true,然后重新运行。不要在普通窗口中依赖命令行参数。 - 如果你的测试依赖代理所在地,请让浏览器的时区和区域设置与之一致(使用 Playwright 选项;在普通浏览器中则修改系统时区和浏览器语言),然后重新检查 Timezone Detection。
- 阅读指纹扣分项,把代理造成的扣分与测试环境造成的扣分区分开。
- 每次修改后,在你实际要用的浏览器配置中重新运行所有测试。
ipvolt 做这些检查是为了自己的上线前测试。服务仍在开发中,将于 11 月初开放访问。如果你想在开放时收到通知,请加入早期访问名单。访问开放时发送一封邮件。仅此而已。
参考来源
- Chromium: Proxy support in Chrome (net/docs/proxy.md)
- RFC 8828: WebRTC IP Address Handling Requirements
- Chrome Enterprise policy: WebRtcIPHandling
- Chromium policy definition: WebRtcIPHandling.yaml
- Chrome extensions API: chrome.privacy (webRTCIPHandlingPolicy)
- Chrome Web Store: WebRTC Network Limiter
- Chromium source: content_switches.cc (force-webrtc-ip-handling-policy)
- Chromium source: headless_web_contents_impl.cc
- Chromium source: renderer_preferences_util.cc
- Firefox source: modules/libpref/init/all.js (media.peerconnection prefs)
- Firefox source: StaticPrefList.yaml
- ToDetect IP Quality Check
- ToDetect DNS Leak Test
- ToDetect WebRTC Leak Test
- ToDetect Timezone Detection
- ToDetect Browser Fingerprint Checker
- ToDetect Bot Detection