代理
fetchWithEvent(event, url, init?)
发起一个携带事件上下文的 fetch 请求。
行为取决于目标:
内部 url(以 / 开头)通过 event.app.fetch()(子请求)分派,永远不会离开进程。它会继承传入请求经过筛选的请求头(通过 getProxyRequestHeaders)和运行时元数据(ip、waitUntil 等)。它始终基于应用自身的源解析:开头连续的分隔符(//host/x、/\host/x)会被折叠为单个 /,而不会被读取为主机。
外部 url 使用原生的 fetch(url, init) 不做任何修改发送——不会继承事件的请求头和上下文(将 Cookie 或授权信息转发到任意主机是不安全的)。当 init.body 是流且未设置 duplex 时,会将其设置为 duplex: "half",这是 Node 的 fetch 所要求的。
安全性: 切勿将未经过滤的用户输入作为 url 传递。调用方负责验证和限制 URL。
getProxyRequestHeaders(event)
获取请求头对象,但不包含已知会在代理时导致问题的头部。
proxy(event, target, opts)
向目标 URL 发起代理请求,并将响应返回给客户端。
如果 target 以 / 开头,则请求通过 event.app.fetch()(子请求)在内部分派,永远不会离开进程。这会绕过任何外部安全层(反向代理认证、IP 白名单、mTLS)。
上游 3xx 响应默认会直接传递给客户端,而不是跟随重定向。设置 fetchOptions: { redirect: "follow" } 可改为跟随重定向——但对于流式请求体,跟随重定向可能会失败,因为请求体一旦被消费就无法重新播放。(通过 event.app.fetch() 发起的内部子请求永远不会跟随重定向。)
限制(继承自 fetch):上游响应体始终会被解压缩(端到端不会保留压缩状态);host 请求头会被重写为目标地址(通过 forwardHeaders: ["host"] 保留该请求头在 Node.js 上有效,但在其他运行时中可能会被忽略);Unix 套接字、TLS 选项或连接代理需要使用特定于运行时的扩展机制(例如,在 Node.js 中于 fetchOptions 里使用 undici 的 dispatcher)。在浏览器和服务工作线程运行时中,对于外部目标,redirect: "manual" 会产生无法中继的不透明重定向(将返回 502)——请在这些环境中设置 fetchOptions: { redirect: "follow" }。
安全性: 切勿将未经清理的用户输入作为 target 传递。调用方负责验证和限制目标 URL(例如,将主机列入允许列表、阻止内部路径、强制使用指定协议)。
凭据转发: proxy 不会自动转发传入请求的请求头——仅会原样发送调用方通过 opts.headers(或 fetchOptions.headers)显式传入的请求头。不要将客户端的 Cookie 或 Authorization 请求头转发到不完全信任的上游。请注意,opts.filterHeaders 在这里不起作用——它只会由 proxyRequest 应用(后者会转发传入请求头,并提供 filterHeaders: ["cookie", "authorization"] 作为缓解措施)。
proxyRequest(event, target, opts)
将传入请求代理到目标 URL。
如果 target 以 / 开头,则请求会通过 event.app.fetch() 由应用路由器在内部处理,而不是发起外部 HTTP 请求。此类目标始终基于应用自身的源解析:开头连续的分隔符(//host/x、/\host/x)会被折叠为单个 /,而不会被读取为主机。
请求体会以流的方式传输到目标端,不会被缓冲。按照 Fetch 标准,请求体只能被消费一次,因此如果你事先读取了它(例如通过 readBody()、readFormData() 或读取请求体的中间件),流就会被锁定,代理会失败。如果你需要在检查请求体的同时继续代理,请从副本中读取,并保持原始事件不变。
上游 3xx 响应默认会直接传递给客户端,而不是跟随重定向。设置 fetchOptions: { redirect: "follow" } 可改为跟随重定向——但对于流式请求体,跟随重定向可能会失败,因为请求体一旦被消费就无法重新播放。
安全性: 切勿将未经清理的用户输入作为 target 传递。调用方负责验证和限制目标 URL(例如,将主机列入允许列表、阻止内部路径、强制使用指定协议)。代理不受信任的输入时,可以考虑使用 bodyLimit() 中间件,以防止过大的请求体消耗过多资源。
凭据转发: 传入请求的 Cookie 和 Authorization 请求头会原样转发到 target。对于具有相同信任关系的反向代理,这是正确的行为;但如果上游并不完全可信,则会泄露客户端凭据。将请求代理到不完全信任的上游时,请使用 filterHeaders: ["cookie", "authorization"] 将其移除。(这不同于 fetchWithEvent,后者永远不会将事件的请求头转发到外部 URL。)
示例:
app.all("/proxy", async (event) => {
const body = await event.req.clone().json(); // 从副本中读取
// ...检查请求体...
return proxyRequest(event, "/target"); // 原始流仍然保持完整
});