跳到主要内容
NextProxyNextProxy文档

Node.js

undici、axios、got 和原生 fetch 的代理接入,含连接池控制与轮换。

Node.js 的内置 fetchhttp 模块不读 HTTP_PROXY 环境变量,也不能直接配代理。必须显式用一个 agent 或 dispatcher。

undici(推荐)

Node 18+ 内置了 undici 作为 fetch 的实现,它的 ProxyAgent 是目前最干净的方案。

shell
npm install undici
javascript
import { ProxyAgent, request } from 'undici';

const PROXY = 'http://USERNAME-country-US-session-job1-time-30:PASSWORD@GATEWAY_HOST:58971';

const dispatcher = new ProxyAgent(PROXY);

const { statusCode, body } = await request('https://api.ipify.org?format=json', {
  dispatcher,
});
console.log(statusCode, await body.json());

await dispatcher.close();

配合全局 fetch

javascript
import { ProxyAgent, setGlobalDispatcher } from 'undici';

setGlobalDispatcher(
  new ProxyAgent('http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971'),
);

// 之后所有的 fetch 都走代理
const response = await fetch('https://api.ipify.org');
console.log(await response.text());

轮换 IP

不带 session,并且每次用新的 dispatcher:

javascript
import { ProxyAgent, request } from 'undici';

const PROXY = 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971';

for (let i = 0; i < 5; i += 1) {
  const dispatcher = new ProxyAgent(PROXY);
  const { body } = await request('https://api.ipify.org', { dispatcher });
  console.log(await body.text());
  await dispatcher.close();
}

并发 + 每任务独立会话

javascript
import { ProxyAgent, request } from 'undici';

const GATEWAY = 'GATEWAY_HOST:58971';
const USER = 'USERNAME';
const PASSWORD = 'PASSWORD';

function proxyFor(sessionId, country = 'US', minutes = 30) {
  const user = `${USER}-country-${country}-session-${sessionId}-time-${minutes}`;
  return `http://${user}:${PASSWORD}@${GATEWAY}`;
}

async function fetchWith(sessionId) {
  const dispatcher = new ProxyAgent(proxyFor(sessionId));
  try {
    const { body } = await request('https://api.ipify.org', { dispatcher });
    return await body.text();
  } finally {
    await dispatcher.close();
  }
}

const results = await Promise.all(
  // session 值只允许 [A-Za-z0-9.-],用点号不用下划线
  Array.from({ length: 8 }, (_, i) => fetchWith(`task.${i}`)),
);
console.log(results);

控制连接池

javascript
import { ProxyAgent } from 'undici';

const dispatcher = new ProxyAgent({
  uri: 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971',
  connections: 16,
  // 网关空闲超时是 5 分钟,客户端设得更短能避免用到已被关闭的连接
  keepAliveTimeout: 60_000,
  keepAliveMaxTimeout: 240_000,
});

axios

axios 的 proxy 配置项对 HTTPS 请求处理得不好,实践中应该用 httpsAgent

shell
npm install axios https-proxy-agent
javascript
import axios from 'axios';
import { HttpsProxyAgent } from 'https-proxy-agent';

const PROXY = 'http://USERNAME-country-US-session-job1-time-30:PASSWORD@GATEWAY_HOST:58971';
const agent = new HttpsProxyAgent(PROXY);

const client = axios.create({
  httpAgent: agent,
  httpsAgent: agent,
  // 必须关掉,否则 axios 会尝试自己处理代理并和 agent 冲突
  proxy: false,
  timeout: 30_000,
});

const { data } = await client.get('https://api.ipify.org?format=json');
console.log(data);

got

shell
npm install got hpagent
javascript
import got from 'got';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';

const PROXY = 'http://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971';

const client = got.extend({
  agent: {
    http: new HttpProxyAgent({ proxy: PROXY, keepAlive: true }),
    https: new HttpsProxyAgent({ proxy: PROXY, keepAlive: true }),
  },
  timeout: { request: 30_000 },
  retry: {
    limit: 3,
    // 先区分代理握手错误和目标响应;仅对可安全重放的请求退避重试
    statusCodes: [429, 500, 502, 503, 504],
  },
});

console.log(await client('https://api.ipify.org').text());

SOCKS5

shell
npm install socks-proxy-agent
javascript
import { SocksProxyAgent } from 'socks-proxy-agent';
import { request } from 'undici';

// socks5h 让代理解析域名
const agent = new SocksProxyAgent(
  'socks5h://USERNAME-country-US:PASSWORD@GATEWAY_HOST:58971',
);

// 配合 node:https
import https from 'node:https';

https.get('https://api.ipify.org', { agent }, (response) => {
  response.setEncoding('utf8');
  response.on('data', (chunk) => process.stdout.write(chunk));
});

原生 http 模块 + CONNECT

需要完全控制协议交互时(比如自己实现指纹)可以手工发 CONNECT:

javascript
import http from 'node:http';
import tls from 'node:tls';

const USER = 'USERNAME-country-US';
const PASSWORD = 'PASSWORD';
const auth = Buffer.from(`${USER}:${PASSWORD}`).toString('base64');

const req = http.request({
  host: 'GATEWAY_HOST',
  port: 58971,
  method: 'CONNECT',
  path: 'api.ipify.org:443',
  headers: { 'Proxy-Authorization': `Basic ${auth}` },
});

req.on('connect', (res, socket) => {
  if (res.statusCode !== 200) {
    console.error('隧道建立失败:', res.statusCode);
    socket.destroy();
    return;
  }

  const tlsSocket = tls.connect({ socket, servername: 'api.ipify.org' }, () => {
    tlsSocket.write('GET / HTTP/1.1\r\nHost: api.ipify.org\r\nConnection: close\r\n\r\n');
  });
  tlsSocket.pipe(process.stdout);
});

req.end();

从提取接口拿代理池

javascript
const API = 'https://api.example.com/api/v1/proxy/extract';

async function fetchProxyPool(apiKey, count = 10) {
  const params = new URLSearchParams({
    apikey: apiKey,
    num: String(count),
    type: 'json',
    country: 'US',
    session: 'sticky',
    time: '10',
  });

  const response = await fetch(`${API}?${params}`);
  if (!response.ok) {
    throw new Error(`提取失败: ${response.status} ${await response.text()}`);
  }
  const { data } = await response.json();
  // 免密端口,不需要凭据
  return data.map((item) => `http://${item.ip}:${item.port}`);
}

const pool = await fetchProxyPool('YOUR_API_KEY', 5);
console.log(pool);

排错清单

现象常见原因
请求直接绕过了代理用了原生 fetch 但没设 dispatcher;axios 忘了 proxy: false
407凭据错;用户名里的 - 被误处理
400invalid_proxy_parameters:参数格式、顺序或依赖错误
402traffic_exhausted / account_quota_exhausted / quota_exhausted:流量或额度不足
403account_disabled / account_expired / access_denied / target_denied:账号或策略拒绝
429connection_limit:降低并发和建连频率
502出口连接失败
503暂无可用线路或服务暂不可用
504建立代理连接超时
ECONNRESET 频发连接池 keepAlive 超过网关的 5 分钟空闲超时
IP 一直不变dispatcher/agent 被复用,连接没重建

HTTP / CONNECT 失败可读取 X-NextProxy-Error 与 JSON 的 error.code;目标网站返回的状态和 CONNECT 握手错误需分别处理。完整映射见 错误码对照

这篇解决你的问题了吗?