对于经过正向代理的普通 HTTP 请求,Node 连接到代理,并把目标 URL 写进请求行。三个值描述这同一个请求:

值示例
TCP 连接端点proxy.example:8080
请求目标http://example.com/news?q=proxy
Host 头example.com

这个 Node 22 示例把这种分离写得很清楚:

const http = require('node:http');
const destination = new URL('http://example.com/news?q=proxy');

const request = http.get({
  hostname: 'proxy.example',
  port: 8080,
  path: destination.href,
  headers: { Host: destination.host },
  signal: AbortSignal.timeout(15000),
}, (response) => {
  console.log(response.statusCode);
  response.on('error', console.error);
  response.resume();
});
request.on('error', console.error);

端点是占位符;要做一次公开的 HTTP 测试,可以从HTTP 代理列表里挑一个候选,并检查它的响应。示例在打印状态码后把响应读空;应用程序也可以改为消费正文。查询串仍然是请求目标的一部分。

为什么完整 URL 要放在那里

源服务器通常收到的是 /news?q=proxy 这样的目标。正向代理也需要知道目的地,因此 HTTP/1.1 定义了绝对形式的请求目标:

GET http://example.com/news?q=proxy HTTP/1.1
Host: example.com

按照 HTTP/1.1 的规则,收到绝对形式的代理在转发时使用 URI 的授权部分,并替换掉不一致的接收到的 Host 字段。发送一致的 Host 头仍能让发出的请求保持无歧义。Node 的连接主机名只是中间跳,所以要显式设置这个头,而不是依赖连接地址。

如果需要认证,代理的凭据属于它自己这一跳。Node 的 auth 请求选项会为资源生成 Authorization 头;它并不是通用的代理登录选项。当交互更复杂时,请使用代理要求的方案和合适的代理客户端。

HTTPS 还要加一条隧道

这个示例只覆盖普通 HTTP 转发。典型的通过 HTTP 代理的 HTTPS 连接会先请求 CONNECT example.com:443,然后在已接受的隧道里向目标启动 TLS。把示例 URL 改成 HTTPS 并不会自动增加这些阶段。

对于 HTTPS 或混合负载,请使用具备所需隧道支持的、仍在维护的代理 agent 或 dispatcher。对于兼容的 Node 版本,一个选择是带 ProxyAgent 的 Undici fetch。当你需要控制或理解的正是普通 HTTP 交互本身时,就保留这个小巧的核心客户端示例。

相关文章