# Node.js 中使用旋转住宅代理：undici、axios、got 和 SOCKS5

> Node.js 中使用旋转住宅代理：undici ProxyAgent 配合 fetch，axios 和 got 支持代理代理，SOCKS5，单主机限制。语法校验代码。

- URL: https://crawlrelay.com/zh/docs/node/
- 最后更新: 2026-10-13
- Publisher: CrawlRelay (https://crawlrelay.com)

Node 内置 fetch 默认忽略代理设置，除非传入调度器。这些方法库分别适用，加上无依赖的单主机限制器。

**快速回答 — 如何在 Node.js 里使用旋转代理？**

使用 fetch 时创建 undici `ProxyAgent` 用于 `http://gw.crawlrelay.com:7777` 并作为 `dispatcher` 传入。axios 或 got 传入代理代理如 `https-proxy-agent` 或 `hpagent`，并在 axios 里设置 `proxy: false`。**CrawlRelay** 住宅代理通过用户名读取国家和会话，来自 US$0.50/GB。支持加密货币支付，无需 KYC。

## Node.js 配置速览

| 属性 | 值 |
| --- | --- |
| fetch / undici | `new ProxyAgent({ uri, token })`，然后 `fetch(url, { dispatcher })` |
| axios | `httpsAgent: new HttpsProxyAgent(url)` 和 `proxy: false` |
| got | 从 hpagent 获取 `agent: { https: new HttpsProxyAgent({ proxy }) }` |
| SOCKS5 | 使用端口 7778 的 `socks5h://` URL 获取 `socks-proxy-agent` |
| 每次请求使用新 IP | 每个请求使用新的代理或新的会话 ID；池化连接保持其出口 |
| 模块格式 | 示例为 ES 模块（`.mjs` 或 `"type": "module"`），支持顶层 await |

## 如何在 Node 的 fetch 中使用代理？

Node 的全局 `fetch` 基于 undici，但默认不支持读取 `HTTP_PROXY` 或 `HTTPS_PROXY`。安装 undici 并传入 `ProxyAgent` 作为调度器。`token` 字段通过 `Proxy-Authorization` 头部发送凭据，因此密码不会出现在可能被记录的 URL 中。

*undici ProxyAgent 配合 fetch*

```javascript
import { ProxyAgent, fetch } from "undici";

const credentials = Buffer.from("USERNAME-country-us:PASSWORD").toString("base64");
const dispatcher = new ProxyAgent({
  uri: "http://gw.crawlrelay.com:7777",
  token: `Basic ${credentials}`,
});

const res = await fetch("https://api.ipify.org?format=json", {
  dispatcher,
  signal: AbortSignal.timeout(30000),
});
console.log(res.status, await res.json());
await dispatcher.close();
```

## 如何在 axios 中使用代理？

不要依赖 axios 自带的 `proxy` 选项用于 HTTPS 站点：它会直接发送请求到代理而不建立 CONNECT 隧道，很多代理会拒绝。需禁用该选项（`proxy: false`），并传入一个可以打开正确隧道的代理。

*axios + https-proxy-agent*

```javascript
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://USERNAME-country-de:PASSWORD@gw.crawlrelay.com:7777");
const client = axios.create({ httpsAgent: agent, httpAgent: agent, proxy: false, timeout: 30000 });

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

## 如何在 got 中使用代理？

got 每种协议只接受一个代理。hpagent 遵循 Node 的 `http.Agent`：默认关闭 keep-alive，除非传入 `keepAlive: true`。想让每个请求使用新连接，也就是通常用新出口时，关闭它或按下面设置的 `keepAlive: false`。

*got + hpagent*

```javascript
import got from "got";
import { HttpsProxyAgent } from "hpagent";

const proxy = "http://USERNAME-country-fr:PASSWORD@gw.crawlrelay.com:7777";
const body = await got("https://api.ipify.org", {
  agent: { https: new HttpsProxyAgent({ proxy, keepAlive: false }) },
  timeout: { request: 30000 },
  retry: { limit: 2, statusCodes: [429, 502, 503, 504] },
}).text();
console.log(body);
```

## 如何在 Node.js 中使用 SOCKS5？

使用带有 `socks5h://` 协议的 `socks-proxy-agent` ，这样主机名会在出口解析。它兼容 Node 的 `http` 和 `https` 模块，也支持接受代理的任何库。

*socks-proxy-agent 配合 node:https*

```javascript
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new SocksProxyAgent("socks5h://USERNAME-country-gb:PASSWORD@gw.crawlrelay.com:7778");

const req = https.get("https://api.ipify.org", { agent, timeout: 30000 }, (res) => {
  let body = "";
  res.on("data", (chunk) => (body += chunk));
  res.on("end", () => console.log(res.statusCode, body));
});
req.on("timeout", () => req.destroy(new Error("timeout after 30 s")));
req.on("error", (err) => console.error(err.code, err.message));
```

## 如何在不借助库的情况下限制每个主机的并发数？

`Promise.all` 在 10,000 个 URL 上会同时打开 10,000 个请求。对每个主机设置小并发限制可以让每个网站保持合理负载，同时整个爬取任务仍是并行的。以下版本无依赖，兼容任意客户端。

*每主机并发限制 + undici*

```javascript
import { ProxyAgent, fetch } from "undici";

const PER_HOST = 4;
const active = new Map();
const waiting = new Map();
const dispatcher = new ProxyAgent("http://USERNAME-country-us:PASSWORD@gw.crawlrelay.com:7777");

async function withHostSlot(url, task) {
  const host = new URL(url).hostname;
  while ((active.get(host) ?? 0) >= PER_HOST) {
    await new Promise((resolve) => {
      if (!waiting.has(host)) waiting.set(host, []);
      waiting.get(host).push(resolve);
    });
  }
  active.set(host, (active.get(host) ?? 0) + 1);
  try {
    return await task();
  } finally {
    active.set(host, active.get(host) - 1);
    waiting.get(host)?.shift()?.();
  }
}

const urls = ["https://example.com/", "https://example.org/", "https://example.net/"];
const results = await Promise.all(
  urls.map((url) =>
    withHostSlot(url, async () => {
      const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(30000) });
      return [url, res.status, (await res.arrayBuffer()).byteLength];
    }).catch((err) => [url, err.name, 0])
  )
);
console.table(results);
await dispatcher.close();
```

## 为什么我会遇到重复 IP 或 ECONNRESET？

代理池化连接，连接保持其打开时的出口。要实现每次请求新 IP，需要每次请求创建新代理并在用后关闭，或者在代理中关闭 keep-alive，或者在每个请求的用户名中放入唯一`-session-<id>`。

`ECONNRESET` 和 `socket hang up` 通常来自重用已被远端关闭的池化连接，或出口掉线。GET 请求遇到此类错误时，重试一次新连接。[连接重置页面](https://crawlrelay.com/zh/docs/errors/connection-reset/) 有详细介绍。

## Node.js 与代理：常见问题

### Node.js 的 fetch 支持 HTTP_PROXY 吗？

当前大多数项目常用版本默认不支持：全局 fetch 会忽略代理环境变量，除非传入调度器。使用上述的 undici `ProxyAgent`，或者如果希望进程内每个请求都通过代理，则用 undici 的 `setGlobalDispatcher()` 设置全局调度器。

### 为什么 axios 代理 HTTPS 站点时会失败？

axios 内建的 `proxy` 选项把 HTTPS 请求以许多代理拒绝的方式发送给代理，未建立 CONNECT 隧道。需设置 `proxy: false` 并传入 `HttpsProxyAgent` 作为 `httpsAgent`。请求才会通过正确隧道，TLS 保持在你的代码和目标站点之间。

### 如何用一个 Node.js 进程发送多个国家的请求？

为每个国家创建一个代理，每个代理使用不同用户名如 `USERNAME-country-de` 或 `USERNAME-country-jp`，每个请求选用不同代理。代理成本低，分开存储连接池，从而分开出口。

### 在 Node.js 中我该设置哪个超时？

务必设置超时：fetch 用 `AbortSignal.timeout(30000)`，axios 用 `timeout: 30000`，got 用 `timeout: { request: 30000 }`。HTML 页面用 30 秒足够；慢目标和移动出口可达 60 秒，且建议遇超时在新连接上重试一次，而非等待更久。

**连接你的 Node.js 爬虫** US$5.94 适用于 10 GB，US$0.50/GB 来自 50 GB。 [查看价格](https://crawlrelay.com/zh/pricing/) · [Puppeteer 指南](https://crawlrelay.com/zh/docs/puppeteer/)

---

CrawlRelay: CrawlRelay 是为开发者设计的住宅代理和 4G/5G 移动代理轮换服务，适用于爬虫和 AI 数据管道：按需付费且流量永不过期，最低流量从 US$0.50/GB 起，支持加密货币支付，无需上传身份证明。无需 KYC —— 无需身份证、自拍和任何证件。支持加密货币支付。
