Puppeteer 控制的是 Chrome/Chromium 浏览器请求。整个浏览器统一使用代理时,最常见的入口是 --proxy-server;只想让某个 BrowserContext 使用独立代理时,可以配置 proxyServer;HTTP 代理要求账号密码时,再通过 page.authenticate() 提供凭证。本文只解决代理配置、认证、出口 IP 验证和连接失败排查,不展开代理池或轮换调度。
| 需求 | 推荐方式 | 主要检查项 |
|---|---|---|
| 整个浏览器统一走代理 | --proxy-server | 代理协议、Host、Port |
| 某个 BrowserContext 单独走代理 | proxyServer | 代理是否配置在正确 Context |
| 部分 Host 不走代理 | --proxy-bypass-list / proxyBypassList | 绕过规则与目标 Host |
| HTTP 代理需要账号密码 | page.authenticate() | 凭证、认证方式与服务商规则 |
| 判断代理是否真正生效 | Direct vs Proxy 对比 | 公网出口 IP 与地区 |
下文按本文发布时的 Puppeteer 25.11 官方文档核对。示例使用 Node.js ESM;先执行 npm install puppeteer,再保存为 .mjs 文件运行。Puppeteer 和 Chrome 的接口可能随版本调整,发布或升级项目时应再核对官方文档。
一、Puppeteer 代理有哪些配置方式?
整个浏览器:--proxy-server
要让这次启动的 Chrome/Chromium 统一经过同一个代理,可以通过 Puppeteer 的 args 把 --proxy-server 传给浏览器进程:
const browser = await puppeteer.launch({
args: ['--proxy-server=http://proxy.example.com:7778']
});
args 是额外浏览器启动参数的入口。代理配置在浏览器启动时确定,不是页面打开以后再给某个请求临时增加的普通请求头。完整的启动选项可参考 Puppeteer LaunchOptions 文档。
某个 BrowserContext:proxyServer
同一个浏览器进程里,如果不同隔离上下文需要不同代理,可在创建 BrowserContext 时设置 proxyServer:
const context = await browser.createBrowserContext({
proxyServer: 'http://proxy.example.com:7778'
});
当前 Puppeteer BrowserContextOptions 同时列出了 proxyServer 和 proxyBypassList。这个入口适合隔离 Context 的代理配置,但不等于 Puppeteer 提供了一个“任意 Page 随时更换代理”的通用 API。
浏览器代理不会自动接管 Node.js 请求
Puppeteer 的代理设置影响 Chrome/Chromium 页面请求,不会自动给同一脚本里的 Axios、Node.js fetch 或其他 HTTP Client 配置代理。page.goto() 和 Node.js 客户端请求属于不同网络路径,排查出口时必须分别验证。
--proxy-server,独立 Context 使用 proxyServer;绕过规则与 HTTP 代理认证分别配置。二、怎么用 --proxy-server 配置 HTTP / HTTPS 代理?
最小可运行示例
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({
headless: true,
args: [
'--proxy-server=http://proxy.example.com:7778'
]
});
try {
const page = await browser.newPage();
const response = await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
console.log('status:', response?.status());
} finally {
await browser.close();
}
把示例 Host 和 Port 换成代理服务商提供的实际端点。代码能运行且页面能打开,只能证明请求完成,不能证明浏览器已经使用预期出口;第七节还会用公网 IP 做 A/B 验证。
代理地址里的 protocol、host、port 分别是什么?
| 字段 | 示例 | 容易误解的地方 |
|---|---|---|
| protocol | http | 描述客户端到代理端点的协议,不是目标网站协议 |
| host | proxy.example.com | 不要把完整账密 URL 当成 Host |
| port | 7778 | 以服务商提供的端口为准,不要凭协议名称猜端口 |
HTTPS 页面必须使用 HTTPS 代理吗?
不必须。--proxy-server=http://proxy.example.com:7778 可以用于访问 https://example.com。目标页面使用 HTTPS,不代表代理端点本身就是 HTTPS Proxy;只有代理服务商明确提供受 TLS 保护的代理端点时,才应按其说明使用 https://。
三、哪些地址不想走代理?怎么配置 bypass
浏览器级代理可以同时配置 bypass list,让匹配的目标绕过代理。命令行规则可写成以分号或逗号分隔的列表:
const browser = await puppeteer.launch({
args: [
'--proxy-server=http://proxy.example.com:7778',
'--proxy-bypass-list=*.internal.example.com;api.internal.example.com'
]
});
使用 BrowserContext 代理时,对应字段是字符串数组:
const context = await browser.createBrowserContext({
proxyServer: 'http://proxy.example.com:7778',
proxyBypassList: [
'*.internal.example.com',
'api.internal.example.com'
]
});
Chromium 的 代理配置文档说明,bypass rules 决定哪些 URL 在已有代理配置时仍直接连接。bypass 是目标绕过规则,不是代理认证白名单,也不是安全访问控制。另外,Chrome 对 localhost 和部分链路本地地址存在隐式绕过规则,验证代理时不要默认用 localhost 作为目标。
四、一个浏览器里不同 Context 怎么使用不同代理?
BrowserContext 是同一浏览器进程中的隔离用户上下文。Cookies、LocalStorage 等会在 Context 之间隔离;当前版本还允许在创建 Context 时分别指定代理:
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const usContext = await browser.createBrowserContext({
proxyServer: 'http://proxy-us.example.com:7778'
});
const jpContext = await browser.createBrowserContext({
proxyServer: 'http://proxy-jp.example.com:7778'
});
const usPage = await usContext.newPage();
const jpPage = await jpContext.newPage();
await Promise.all([
usPage.goto('https://api.ipify.org?format=json'),
jpPage.goto('https://api.ipify.org?format=json')
]);
} finally {
await browser.close();
}
这段代码只展示 Context 与代理的对应关系。实际项目还要分别处理认证、超时和出口验证。如果该 Context 使用的是需要用户名和密码认证的 HTTP 代理,仍应在对应 Page 首次导航前调用 page.authenticate();proxyServer 只负责指定代理端点。不要把“Context 可配置独立代理”理解成“同一个 Page 可以在请求进行中随意切换代理”;代理选择、会话隔离和页面生命周期仍应一起设计。
五、Puppeteer 代理账号密码怎么填写?
不要把账号密码直接塞进 --proxy-server
不要把下面这种地址当成 Chrome 手动代理认证的标准写法:
http://username:password@proxy.example.com:7778
Chromium 官方说明,Chrome 不会使用嵌在手动代理设置中的明文凭证。代理服务器地址和认证凭证应分开处理,避免出现地址看似完整、浏览器却仍弹出认证或返回 407 的情况。
使用 page.authenticate() 提供凭证
const page = await browser.newPage();
await page.authenticate({
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
});
await page.goto('https://example.com', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
page.authenticate() 的官方定义是为 HTTP authentication 提供凭证,并不是只用于代理认证的专用 API。Puppeteer Page.authenticate() 文档还提醒,启用认证会在后台开启 request interception,可能影响性能。凭证应在访问需要认证的页面前设置,并通过环境变量或 Secret 管理,不要把真实密码写死在源码或日志里。
账号密码已填,为什么仍然返回 407?
407 Proxy Authentication Required 表示请求到达了要求认证的代理,但当前请求没有提供可接受的代理凭证。优先核对 Username、Password 和代理认证方式;如果服务商还要求来源 IP 白名单,或把地区、Session 等参数编入 Username,再继续核对这些服务商侧规则。若仍无法定位,可继续查看 407 代理认证专项排查,这里不重复 URL 编码和认证头的完整故障树。
六、HTTP、HTTPS、SOCKS5 在 Puppeteer 里怎么选?
| 代理类型 | Puppeteer / Chrome 常见入口 | 主要注意点 |
|---|---|---|
| HTTP | --proxy-server=http://proxy.example.com:7778 | 最常见;也能用于访问 HTTPS 页面 |
| HTTPS Proxy | --proxy-server=https://proxy.example.com:7778 | 仅在代理端点本身支持 TLS 时使用 |
| SOCKS5 | --proxy-server=socks5://proxy.example.com:1080 | Chrome 会让 SOCKS5 代理解析目标域名,但不支持 SOCKS5 认证方法 |
HTTP 代理可以代理 HTTPS 页面;SOCKS5 认证也不能直接套用 HTTP 代理的 page.authenticate() 逻辑。Chromium 当前文档明确指出,Chrome 的 SOCKS5 代理会在代理侧解析目标域名,但不支持 SOCKS5 authentication methods。如果服务商提供的 SOCKS5 端点必须使用账号密码,不能只照抄上面的浏览器启动参数,应先确认浏览器兼容的接入方式或使用受控的中间转发方案。
如果还不确定 HTTP、HTTPS 与 SOCKS5 的连接差异,可以先看站内的 代理协议区别与选型指南。本文只讨论这些协议在 Puppeteer 里的配置入口。
七、怎么验证 Puppeteer 浏览器真的经过代理?
页面能打开,不等于代理已经生效。应在同一台运行 Node.js 的机器上,用同一个 IP 查询接口分别执行 Direct 与 Proxy,两次只改变代理配置。下面示例把代理凭证作为可选参数;如果当前使用纯 IP 白名单认证,可以不传 credentials。
import puppeteer from 'puppeteer';
async function readEgressIP({ proxyServer, credentials } = {}) {
const browser = await puppeteer.launch({
headless: true,
args: proxyServer ? [`--proxy-server=${proxyServer}`] : []
});
try {
const page = await browser.newPage();
if (credentials) {
await page.authenticate(credentials);
}
await page.goto('https://api.ipify.org?format=json', {
waitUntil: 'domcontentloaded',
timeout: 30000
});
const text = await page.$eval('body', el => el.innerText);
return JSON.parse(text).ip;
} finally {
await browser.close();
}
}
const direct = await readEgressIP();
const viaProxy = await readEgressIP({
proxyServer: 'http://proxy.example.com:7778',
credentials: {
username: process.env.PROXY_USER,
password: process.env.PROXY_PASS
}
});
console.log({ direct, proxy: viaProxy });
正常使用独立代理出口时,Proxy 返回的公网地址通常应与 Direct 不同。如果配置了指定国家或地区,再使用同一个 GEO 数据来源核对地理信息;“IP 发生变化”和“地区符合预期”是两项不同的验收。固定代理、Sticky Session 与轮换代理还应按服务商声明的规则重复请求,不要把“每次都换 IP”当成所有产品的正确结果。
如果需要长期保持浏览器出口与会话一致,可以根据实际任务考虑 固定IP出口方案;若还分不清本地 IP、公网 IP 和代理出口,可先参考 本地 IP 与公网出口的判断方法。这里仅说明验证逻辑,不展开代理池与轮换调度。
八、Puppeteer 代理连接失败怎么排查?
先记录 Chrome 错误码、目标 URL、实际使用的代理 Host/Port、Context 和出口 IP,再逐项排查。不要同时修改代理地址、认证、bypass 与超时,否则很难判断是哪一项恢复了连接。
| 错误或现象 | 优先检查 | 处理方向 |
|---|---|---|
ERR_PROXY_CONNECTION_FAILED | Host、Port、代理端点可达性 | 先验证能否连接代理,不急着改页面代码 |
| 407 | 代理凭证、认证方式及服务商侧白名单/用户名规则 | 确认 page.authenticate() 的调用时机并转入 407 专项排查 |
page.goto() timeout | 代理链路、目标响应、等待条件与超时值 | 区分是代理连接慢、目标站慢还是等待条件未满足 |
| 页面能打开但出口 IP 没变 | --proxy-server 是否真正传入 | 用第七节的同接口 A/B 测试复核 |
| Context 出口不对 | proxyServer 是否配置在实际创建 Page 的 Context | 记录 Page 所属 Context,不混用页面引用 |
| 部分站点没走代理 | bypass list 与 Chrome 隐式绕过规则 | 核对目标 Host 的实际匹配结果 |
| HTTPS / 证书错误 | 目标证书、代理端点协议、CA / TLS 链路 | 先判断错误发生在哪一跳,不长期关闭证书验证 |
| Node 请求和页面出口不同 | Node HTTP Client 与 Chrome 网络栈 | 分别配置并分别验证两条请求路径 |
为什么 Node.js 请求和 Puppeteer 页面请求出口不一样?
page.goto()、页面资源加载与页面里的 fetch 走 Chrome/Chromium 网络栈;脚本进程直接执行的 Axios 或 Node.js fetch 不会自动继承 --proxy-server。因此,同一段程序中可能同时出现浏览器出口和 Node.js 直连出口。判断时应分别请求同一个 IP 查询接口,而不是把其中一条路径的结果当成整个进程的出口。
为什么账号密码正确还是 407?
密码正确只是一个条件。来源公网 IP 未加入认证白名单、代理用户名参数缺失、账号状态异常、认证方式不匹配,或 page.authenticate() 在导航后才调用,都可能让请求继续返回 407。先用最小页面与单一代理端点复测,再回到业务代码排查。
九、Puppeteer 代理常见问题
1. --proxy-server 可以直接带用户名密码吗?
不建议,也不能把它当成 Chrome 手动代理认证的可靠方式。Chrome 不使用嵌在手动代理设置中的明文凭证。代理地址放进 --proxy-server,HTTP 认证凭证再通过 page.authenticate() 提供。
2. page.authenticate() 是专门的代理认证 API 吗?
不是。它用于提供 HTTP authentication 凭证,既不是只服务代理认证的专用接口,也不能替代所有代理协议的认证流程。启用后还会开启 request interception,可能带来性能影响。
3. Puppeteer 能不能一个 BrowserContext 使用一个代理?
当前版本可以在创建 BrowserContext 时设置 proxyServer 和 proxyBypassList。Page 必须从对应 Context 创建,认证与出口也应按每个 Context 分别验证。
4. Puppeteer SOCKS5 代理怎么设置?
可使用 --proxy-server=socks5://host:port。Chrome 会把目标域名解析交给 SOCKS5 代理,但当前不支持 SOCKS5 authentication methods;需要账号密码的 SOCKS5 端点不能直接套用 HTTP 代理认证示例。
5. 为什么页面打开成功,但出口 IP 没变化?
先确认启动参数是否实际传入运行中的浏览器,再检查 Page 是否来自配置了代理的正确 Context、目标是否命中 bypass,以及所谓 Direct 基线是否被系统级网络转发接管。最后用同一接口做 A/B 对照。
6. Puppeteer 的代理会影响 Node.js Axios 或 fetch 吗?
不会自动影响。Puppeteer 的浏览器代理处理 Chrome/Chromium 页面流量;Node.js 客户端请求需要单独配置代理,并单独检查公网出口。
最终验收至少确认四项:代理配置作用在预期的 Browser 或 Context、认证可以完成、浏览器公网出口符合预期、Node.js 请求与页面请求没有被误认为同一条链路。页面成功打开只是起点,不是代理配置已经正确生效的最终证据。