You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Puppeteer搭配proxy-chain出现Navigation超时错误,求排查方案

问题描述
  • 代码在不使用proxy-chain时运行正常,但给Puppeteer传入proxy-server参数后触发超时错误:

    TimeoutError: Navigation timeout of 30000 ms exceeded

  • 本地开发环境指定Chromium路径的两种方式:
    PPTR_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    PPTR_EXECUTABLE_PATH=/Applications/Chromium.app/Contents/MacOS/Chromium
    
  • 排查情况:单独用node-fetch搭配相同凭证的proxy-chain运行正常;Puppeteer单独运行正常;proxy-chain单独使用也正常,但两者结合就触发超时。

相关代码:

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
require('dotenv').config();
puppeteer.use(StealthPlugin());

const proxyChain = require('proxy-chain');
const proxy_host = process.env.PROXY_HOST;
const proxy_username = process.env.PROXY_USERNAME;
const proxy_password = process.env.PROXY_PASSWORD;

exports.getIP = async (req, res, next) => {
    let browser;
    let newProxyUrl;

    try {
        const oldProxyUrl = `http://${proxy_username}:${proxy_password}@${proxy_host}`;
        newProxyUrl = await proxyChain.anonymizeProxy(oldProxyUrl);

        browser = await puppeteer.launch({
            headless: "false",
            ignoreHTTPSErrors: true,
            executablePath: process.env.PPTR_EXECUTABLE_PATH || '/usr/bin/chromium',
            args: [
                `--proxy-server=${newProxyUrl}`,
                `--no-sandbox`,
                `--disable-gpu`,
            ]
        });

        const page = await browser.newPage();
        await page.goto('https://nordvpn.com/what-is-my-ip/', { waitUntil: 'networkidle0' });

        const ip = await page.$eval('.Title.h3.mb-6.js-ipdata-ip-address', el => el.textContent);
        const location = await page.$eval('.js-ipdata-location', el => el.textContent);

        res.status(200).json({ success: true, ip: ip, location: location });
    } catch (error) {
        next(error);
    } finally {
        if (browser) {
            await browser.close();
            await proxyChain.closeAnonymizedProxy(newProxyUrl, true);
        }
    }
}
可能的原因及解决方案

1. networkidle0等待条件过于严格

networkidle0要求页面加载完成后0个网络连接持续500ms,但nordvpn的IP检测页存在长期挂起的统计脚本、WebSocket连接等,永远达不到该条件,最终触发超时。

解决方法:
换成更宽松的等待条件,比如domcontentloaded或networkidle2:

await page.goto('https://nordvpn.com/what-is-my-ip/', { waitUntil: 'domcontentloaded' });

或者直接增加超时时间:

await page.goto('https://nordvpn.com/what-is-my-ip/', { waitUntil: 'networkidle0', timeout: 60000 });

2. proxy-chain本地代理端口被拦截或冲突

proxy-chain会在本地启动代理服务器转发请求,可能存在端口被防火墙拦截、端口占用的情况,导致Puppeteer无法通过代理连接外部网络。

排查与解决:
打印newProxyUrl确认代理地址(一般为http://localhost:xxxx),用curl测试代理可用性:

curl -x http://localhost:xxxx https://nordvpn.com/what-is-my-ip/

如果curl也超时,可指定未被占用的端口启动proxy-chain:

newProxyUrl = await proxyChain.anonymizeProxy(oldProxyUrl, { port: 8080 });

3. Puppeteer代理配置遗漏关键参数

Chromium需要额外参数避免本地请求被代理拦截,添加--proxy-bypass-list=<-loopback>,让本地回环地址的请求不经过代理:

args: [
    `--proxy-server=${newProxyUrl}`,
    `--no-sandbox`,
    `--disable-gpu`,
    `--proxy-bypass-list=<-loopback>`
]

4. Stealth插件与代理兼容性冲突

Stealth插件修改的浏览器指纹特征,可能被目标网站或代理服务器识别为异常请求,导致连接被阻断。

排查与解决:
暂时禁用Stealth插件测试:

// puppeteer.use(StealthPlugin()); // 注释该行

如果禁用后正常,可调整插件配置,只启用必要的子插件,避免特征冲突。

5. 代理服务器带宽/延迟不足

node-fetch仅请求单一资源,而Puppeteer加载页面会请求大量图片、脚本、样式等资源,代理服务器的带宽或延迟不足以支撑,导致超时。

解决方法:
更换代理服务器,或全局设置更长的导航超时:

const page = await browser.newPage();
page.setDefaultNavigationTimeout(60000); // 设置全局导航超时为60秒

内容的提问来源于stack exchange,提问作者sebastiansieber

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:35:00