Next.js 16 中 proxy.ts 在 Cloudflare 代理后为何不执行?

👁 77 次阅读

Next.js 16 中 proxy.ts 在 Cloudflare 代理后为何不执行?

来源:https://github.com/vercel/next.js/issues/86122

现象描述

该 issue 报告了一个与 Next.js 16 中间件文件命名相关的运行时问题。标签为 Runtime,当前状态为 open。 复现步骤由报告者给出,核心流程如下: 1. 使用 npx create-next-app@latest 创建新项目(或在任意 Next.js 16.0.3 项目上操作)。 2. 在项目根目录创建 middleware.ts:
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(req: NextRequest) {
  console.log("MIDDLEWARE RUNNING:", req.nextUrl.pathname);
  return NextResponse.next();
}

export const config = {
  matcher: ["/(.*)"],
};
3. 将项目部署在启用 Cloudflare Proxy(orange-cloud ON)之后。 4. 访问任意路由,会看到预期日志:MIDDLEWARE RUNNING: /。 5. 将文件从 middleware.ts 重命名为 proxy.ts,并同步修改导出函数名:
// proxy.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function proxy(req: NextRequest) {
  console.log("PROXY RUNNING:", req.nextUrl.pathname);
  return NextResponse.next();
}

export const config = {
  matcher: ["/(.*)"],
};
6. 删除旧的 middleware.ts,确保项目根目录只保留 proxy.ts。 7. 重新部署到启用 Cloudflare Proxy 的环境。 8. 再次访问任意路由。 实际结果是:没有任何日志输出,proxy.ts 完全不执行,请求行为等同于不存在 middleware/proxy 文件。预期的结果是:proxy.ts 应当与 middleware.ts 表现完全一致,包括在 Cloudflare Proxy 开启时。 报告者进一步补充了当前行为与预期行为的对比:
  • 使用 middleware.ts 时,中间件在本地开发(next dev)、生产环境、以及 Cloudflare Proxy(orange-cloud ON)之后的生产环境都能正确执行。
  • 重命名为 proxy.ts 并修改函数名后:本地开发仍可用,未开启 Cloudflare Proxy 的生产环境仍可用,但在 Cloudflare Proxy 之后部署时完全不执行、无日志、无 rewrite、无 NextResponse 行为,文件仿佛被彻底忽略。报告者强调两个场景之间唯一的差异就是文件名(middleware.ts → proxy.ts)。配置使用了 output: "standalone"。

背景原因

讨论的起点是 icyJoseph 提出该问题是否应跟踪在 opennextjs-cloudflare 仓库的 issue #972。报告者 shivaluma 明确回应:并未使用 OpenNext 或 opennextjs-cloudflare,这是一个完全自托管、直接把 Next.js 部署并通过 Cloudflare 做反向代理的场景,因此不属于 opennextjs-cloudflare 仓库。报告者还强调,该 bug 在没有 OpenNext 的栈中也会出现。 icyJoseph 随后提出另一种可能:本地构建时是否也应失败,是否存在 Dockerfile 步骤或类似环节没有把文件复制进去。报告者给出了复现用仓库链接,包括使用 proxy.ts 的代码仓库与使用 middleware.ts 的分支,以及两个生产应用地址。 讨论后期转向构建清单(manifest)层面的对比。参与讨论的开发者 natelaff 进行了一次直接对比:用 middleware.ts 构建一次,用 proxy.ts 构建一次,逻辑相同、仅重命名文件与导出名。对比结果显示 manifest 并不相同:
  • 使用 middleware.ts(Edge runtime)时:
– middleware-manifest.json 被填充了 matchers、files、env; – functions-config-manifest.json 中 "functions": {}。 icyJoseph 随即指出,对于 nodejs runtime,两者的输出是相同的:相同的 functionsConfigManifest.functions['/_middleware']、相同的 runtime: 'nodejs'、相同的 matchers。无论来自 runtime: 'nodejs' 的 middleware.ts 还是 proxy.ts,输出逐字节一致。因此如果同一平台上一个是可用的而另一个不可用,manifest 并不是解释。讨论由此回到仍未解决的问题上。 截至目前,该 issue 仍为 open,官方尚未合入方案。

排查或量化方法

据 issue 讨论,可用的排查路径集中在构建产物层面,而非运行时代码逻辑层面:
  • 对比构建清单。natelaff 的做法是分别用 middleware.ts 和 proxy.ts 构建同一应用,然后比较 middleware-manifest.json 与 functions-config-manifest.json 的内容差异。
  • 区分 runtime。icyJoseph 指出,在 nodejs runtime 下,两种文件名的输出逐字节一致;差异只在 Edge runtime 下出现。这意味着排查时必须先确认当前使用的是哪种 runtime。
  • 确认部署链路。报告者强调复现不依赖 OpenNext,部署方式为 Next.js 自托管 + Cloudflare 反向代理,因此在排查时应先排除 OpenNext 相关配置的干扰。
参与者随后提出的一个新复现仓库把问题环境进一步明确为:
  • pages router;
  • Next.js 16.1.6;
  • pages extensions;
  • standalone 输出。
icyJoseph 确认了对该复现的两个观察:使用 page extensions 时,webpack 会忽略 proxy 文件;使用 page extensions 时,Turbopack 能找到该文件,但期望一个 function export name。icyJoseph 判断这两个问题实际上都与原报告者的问题不同,并建议复现者另行开 issue。复现者 kamilgrzegorczyk 表示自己没有足够时间走开 issue 的流程,只能确认问题存在,无法继续协助。
对比维度middleware.tsproxy.ts
本地开发(next dev)正常执行正常执行
无 Cloudflare Proxy 的生产环境正常执行正常执行
Cloudflare Proxy 后的生产环境正常执行不执行,无日志
nodejs runtime 构建输出与 proxy.ts 逐字节一致(据 issue 讨论)与 middleware.ts 逐字节一致(据 issue 讨论)
Edge runtime manifestmatchers/files/env 被填充(据 issue 讨论)与 middleware.ts 不同(据 issue 讨论)

完整解决方案(当前处理路径与局限)

需要明确的是:该 issue 目前仍为 open,官方尚未给出修复方案,也未见维护者确认修复版本。以下内容只归纳 issue 讨论中已经出现过的动作与结论,不引入讨论之外的命令或配置。 可确认的现状:
  • 重命名为 proxy.ts 后,在 Cloudflare Proxy 之后不执行的问题未解决。
  • 在 nodejs runtime 下,proxy.ts 与 middleware.ts 的构建输出逐字节一致,因此 manifest 差异不能解释该现象(据 issue 讨论)。
  • 在 Edge runtime 下,两者 manifest 存在差异,但这一差异是否导致 Cloudflare Proxy 场景下的行为差异,讨论中未给出结论。
讨论中出现的定位线索:
functionsConfigManifest.functions['/_middleware']
runtime: 'nodejs'
以上字段在两种文件名的构建输出中相同,可作为排除 manifest 路径的依据。 讨论后期确认的两个相关但不同的问题:
  • 使用 page extensions 时,webpack 忽略 proxy 文件;
  • 使用 page extensions 时,Turbopack 能找到该文件,但期望一个 function export name。
icyJoseph 表示可以为 kamilgrzegorczyk 的问题写一个带失败 e2e 的 PR,但对于原报告者的问题,尚不清楚如何查到底。 局限在于:
  • 官方尚无实质修复方案,也没有合入 PR 的记录;
  • 复现链条依赖 Cloudflare Proxy 环境,无法在纯本地环境稳定重现;
  • 讨论中一度把新复现的问题与原问题混在一起,经 icyJoseph 澄清后确认属于不同 bug;
  • 原报告者的问题在 nodejs runtime 下构建产物与 middleware.ts 一致,使得基于产物的排查手段失效。

常见错误排查

以下条目均来自 issue 讨论中出现过的观察,不代表官方排查指引: 1. 误以为问题属于 OpenNext。报告者明确该复现发生在不使用 OpenNext 的自托管栈中,排查前应先确认技术栈。 2. 误以为只是文件没有被复制进构建产物。icyJoseph 提出过 Dockerfile 步骤是否遗漏文件的可能,但报告者提供了仓库与生产地址,且后续 manifest 对比显示 nodejs runtime 下输出一致。 3. 直接以 manifest 差异作为结论。icyJoseph 指出 nodejs runtime 下两种文件名输出逐字节相同,Edge runtime 下才有差异,不能一概而论。 4. 把 page extensions + Turbopack/webpack 的行为混入本问题。icyJoseph 确认这两个观察与原报告者的问题不同,且建议单独开 issue。 5. 假设在本地构建即可复现。icyJoseph 认为本地构建也应失败,但报告者的实际症状是仅在 Cloudflare Proxy 之后出现,说明本地与部署环境的触发条件并不一致。

实践建议

  • 在确认根因之前,若生产环境依赖 Cloudflare Proxy,保留 middleware.ts 命名是 issue 讨论中唯一被证实可用的路径。
  • 排查时先固定 runtime。nodejs runtime 下两种文件名产物一致,差异只在 Edge runtime 下出现(据 issue 讨论),若不先区分 runtime,manifest 对比会产生误导。
  • 复现时提供完整栈信息。原报告者在讨论早期被迫澄清未使用 OpenNext,说明栈信息缺失会显著拉长定位时间。
  • 把相关但不同的现象分开报告。page extensions 下 webpack 忽略 proxy 文件、Turbopack 期望 function export name 这两个观察,经 icyJoseph 确认与原始问题不同,混在同一 issue 中会分散排查焦点。
  • 若需跟踪进展,可直接查询 GitHub API 中该 issue 与相关 PR 的状态,避免依赖二手转述。

FAQ

Q:这个问题和 OpenNext 有关吗? A:据 issue 讨论,报告者明确未使用 OpenNext 或 opennextjs-cloudflare,问题出现在 Next.js 自托管 + Cloudflare 反向代理的栈中,且在没有 OpenNext 时同样出现。 Q:proxy.ts 在本地开发时也失效吗? A:不是。据报告者描述,本地开发(next dev)与未开启 Cloudflare Proxy 的生产环境下 proxy.ts 仍可正常执行,只有部署在 Cloudflare Proxy 之后时才完全不执行。 Q:manifest 差异是根因吗? A:据 issue 讨论,不一定。icyJoseph 指出在 nodejs runtime 下,middleware.ts 与 proxy.ts 的构建输出逐字节相同,包括 functionsConfigManifest.functions['/_middleware']、runtime: 'nodejs' 与 matchers;差异只在 Edge runtime 的 middleware-manifest.json 与 functions-config-manifest.json 中出现。 Q:page extensions 下 webpack 忽略 proxy 文件是同一个问题吗? A:据 issue 讨论,不是。icyJoseph 确认使用 page extensions 时 webpack 忽略 proxy 文件、Turbopack 找到文件但期望 function export name,这两点与原报告者的问题不同,并建议单独开 issue。 Q:该 issue 目前的状态如何? A:状态为 open。末条实质评论日期为 2026-02-25,icyJoseph 表示可以为其中一个不同的问题写带失败 e2e 的 PR,但对原报告者的问题尚未找到查到底的路径。官方尚未合入修复方案。

订阅 AI 百事知

每周精选 AI 工具评测、行业资讯与使用教程,第一时间送达。

🔗 相关阅读(你可能还想看)

📄 Outlook 节点创建草稿添加附件为何报 UnableToDeserializePostBody?
📄 Flowise 点击市场或聊天页为何整页白屏?
📄 如何解决 Azure AI Search 向量存储的 $filter 递归深度超限错误?
📄 如何解决 shadcn/ui Sidebar 与 Header 重叠的问题?
📄 Next.js 开发服务器为何会 JavaScript heap out of memory?
📄 Nuxt 偶发 Cannot read properties of undefined 报错是怎么回事
📄 如何解决「Visual Studio Code for ipad」相关的问题?
📄 如何在AutoGen中实现企业级代理治理的加密行动收据(AAR)?
📄 记忆内容未清洗直接注入系统提示词,如何防范间接提示注入?
crewAI LiteAgent 将记忆内容直接拼接到系统提示词,存在间接提示注入风险。社区提出分层防御方案,但 iss
🤖 想直接和 AI 对话?
免费体验 DeepSeek V4 等顶级模型,无需注册,随问随答
🎯 立即体验 AI 工具

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

🤖 嘟嘟客服
滚动至顶部
🤖 嘟嘟客服
🤖 嘟嘟客服
';i.value='';m.innerHTML+='
...
';fetch('/dify-chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({query:q,user:'w'+Date.now()})}).then(function(r){return r.json()}).then(function(d){var a=d.answer||'暂时无法回复';m.removeChild(m.lastChild);m.innerHTML+='
'+a+'
'}).catch(function(){m.removeChild(m.lastChild);m.innerHTML+='
网络异常
'})">发送