Playwright/Puppeteer 设置代理后仍暴露本地 IP 怎么排查?
Playwright 或 Puppeteer 设置代理后仍显示本地 IP 时,按启动参数、认证、HTTP 出口、DNS、WebRTC 和子请求定位,并用代码验证配置是否生效。
Playwright 或 Puppeteer 设置代理后仍显示本地 IP,先不要同时修改代理 IP、浏览器版本和脚本。正确顺序是:确认代理参数在浏览器启动前生效,再分别检查页面 HTTP 出口、DNS、WebRTC,以及 WebSocket、Service Worker、下载等子请求。HTTP 出口已经变成代理 IP,但 WebRTC 页面仍显示局域网地址,不等于所有网页请求都在直连,两类结果需要分开判断。
先准备同一组代理参数和两个检测地址
从后台确认协议、主机、端口、账号和密码,测试时不要在日志里输出完整密码。准备两个地址:一个只返回公网出口 IP,另一个是实际目标 URL。先测出口,再测目标站,才能区分“代理没有生效”和“代理已生效但目标站拒绝访问”。
| 要记录的字段 | 用途 |
|---|---|
| 浏览器与框架版本 | 排除版本差异 |
| 有头或无头模式 | 判断模式差异 |
| 代理协议、主机、端口 | 核对启动参数 |
| HTTP 出口 IP | 判断页面请求是否经过代理 IP |
| DNS、WebRTC 结果 | 判断额外暴露面 |
| 失败请求类型 | 区分页面、WebSocket、下载等链路 |
Playwright:在启动浏览器时传入代理参数
在 Playwright 启动阶段配置代理 IP
代码中的 HOST、PORT、USERNAME 和 PASSWORD 代表实际交付参数;调试时使用环境变量或密钥管理工具注入,不要把真实认证信息写进仓库。
```javascript
import { chromium } from 'playwright';
const browser = await chromium.launch({
headless: true,
proxy: {
server: 'http://HOST:PORT',
username: 'USERNAME',
password: 'PASSWORD',
},
});
const context = await browser.newContext();
const page = await context.newPage();
await page.goto('https://api.ipify.org?format=json', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
console.log(await page.textContent('body'));
await browser.close();
```
如何验证 Playwright 代理是否生效
预期结果是页面返回的公网 IP 与代理出口一致。如果仍是本地公网 IP,优先检查 server 的协议、主机和端口,以及是否复用了一个没有代理参数的旧 browser 实例。只在 newContext() 后修改普通请求头,不能替代浏览器启动阶段的代理配置。
排查时先保存浏览器版本、框架版本、运行模式和检测页返回值。若 HTTP 出口已经正确,再单独记录 DNS 和 WebRTC 检测结果。
Playwright 的代理参数可对照官方网络文档。
需要核对代理协议、主机、端口和认证字段时,可查看代理设置帮助。
Puppeteer 中分别配置启动参数与代理认证
配置 Puppeteer 启动参数和代理认证
```javascript
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: true,
args: ['--proxy-server=http://HOST:PORT'],
});
const page = await browser.newPage();
await page.authenticate({
username: 'USERNAME',
password: 'PASSWORD',
});
await page.goto('https://api.ipify.org?format=json', {
waitUntil: 'domcontentloaded',
timeout: 30000,
});
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
```
如何区分 407 与浏览器启动失败
若返回 407,说明请求已经到达代理认证环节,但账号、密码或认证方式不匹配。若浏览器启动失败,先单独验证代理主机和端口是否可连接,再检查 Chromium 启动参数;不要把 407 当成目标站拒绝访问。
HTTP 出口正确后,再查 DNS 和 WebRTC
检测项要按层次解释:
1. HTTP 出口仍是本地公网 IP:代理启动参数未生效、浏览器实例复用错误,或当前请求不在被接管的浏览器内。
2. HTTP 出口正确,DNS 显示本地运营商:继续检查浏览器 DNS 行为、系统解析和客户端设置;不要仅凭 DNS 结果判定页面请求直连。
3. HTTP 出口正确,WebRTC 显示局域网地址:这是浏览器实时通信能力暴露的本地候选地址,需要单独限制或禁用不需要的 WebRTC 功能,但它不自动证明普通 HTTP 请求未走代理。
4. 三项都正确,目标站仍报 403、429 或验证码:转向目标站规则、账号状态、Cookie、请求频率和浏览器环境排查。
DNS 检测应记录解析位置和目标域名,WebRTC 检测应区分公网候选地址与局域网候选地址;两者都要在同一浏览器版本和代理参数下重复验证。
排查未继承浏览器代理的请求类型
在 DevTools 网络面板或框架事件里记录失败 URL 和资源类型。页面主请求、iframe、WebSocket、Service Worker、扩展请求和下载并不一定表现相同。如果脚本还调用了浏览器外部的 Node.js HTTP 客户端,那部分请求不会自动继承浏览器的代理配置,需要在对应客户端中单独设置。
Playwright 可用 page.on('requestfailed', ...) 记录失败请求:
```javascript
page.on('requestfailed', request => {
console.log(request.resourceType(), request.url(), request.failure());
});
```
用四组单变量实验定位本地 IP 泄漏原因
| 对照组 | 只改变什么 | 结果怎么解释 |
|---|---|---|
| A | 有头与无头模式 | 只有一种模式异常,检查版本与功能差异 |
| B | 开启与关闭浏览器扩展 | 关闭后恢复,检查扩展自带网络请求或连接设置 |
| C | 保持脚本不变,只换一个已验证代理 IP | 只在少数出口异常,再查出口质量或地区识别 |
| D | 保持代理 IP 不变,只换检测 URL | 检测页正常、目标站异常,转查目标站规则 |
每组至少记录浏览器版本、时间、出口 IP、状态码和失败请求。不要一次同时换 UA、Cookie、代理 IP 和浏览器版本,否则无法归因。
确认不是代理问题后停止更换代理 IP
如果同一代理 IP 在出口检测页显示正确,而目标站持续返回权限、账号或平台政策提示,应先处理提示;继续更换代理 IP 没有诊断价值。如果只有已异常账号失败,也要先排除账号状态,不建议反复登录测试。
代理参数以 Playwright 官方文档和 Puppeteer 官方文档为准;具体浏览器行为请结合当前版本文档和实际环境验证。
FAQ
系统代理能代替浏览器启动参数吗?
不能一概而论。自动化任务应优先在浏览器启动阶段明确传入代理参数,避免依赖机器上可能变化的系统设置。
WebRTC 显示本地地址就代表网页请求直连吗?
不代表。先用公网出口检测确认 HTTP 请求,再把 WebRTC 作为独立暴露面处理。
出口正确但 WebSocket 失败怎么办?
记录 WebSocket URL、握手状态和错误信息,并确认它是否由当前浏览器实例发起。若是脚本中的独立客户端发起,需要单独配置代理。
什么时候考虑更换代理 IP 类型?
短时多地区公开采样可评估动态住宅代理 IP;需要连续登录环境的任务更适合静态住宅代理 IP。先完成链路排查,再根据会话需求选型。
完成上述检查后,再按业务会话和地区需求评估动态住宅代理 IP。
