工程与系统

Cloudflare + OpenNext 适合什么样的 Next.js 项目:先看运行时边界

这不是泛泛的部署推荐,而是一条适配判断:如果你的 Next.js 项目把太多职责绑进运行时,Cloudflare + OpenNext 很可能先把问题撕开。

Cloudflare + OpenNext 更像一条主动工程路线:它不是简单换个平台上线,而是要求你持续尊重运行时边界和目标环境验证。

最危险的不是红灯,而是一串不完整的绿灯

如果一条发布链路一开始就红得很彻底,反而好处理。你知道它还没准备好,也不会误把它推近发布。真正危险的是前面很多检查都通过,只有目标平台那一层还没有被纳入日常门槛。

这个项目里最典型的一次阻塞,来自 src/proxy.ts。本地构建可以通过,常规 release gate 也没有立刻把它拦下,但切到 Cloudflare / OpenNext 的真实构建时,问题就变得很明确:当前 proxy / middleware 形态不适合继续承担这部分职责。

这种误判很常见。你看到的是绿灯,实际得到的是局部绿灯。它证明当前环境下某些假设成立,却没有证明目标部署环境也接受这些假设。

proxy/middleware 的问题,是职责和运行时绑得太紧

src/proxy.ts 原本承担的事情并不复杂:旧路由跳转、安全响应头、一些全局性的请求处理。放在传统 Next.js 语境里,这种写法看起来很自然,因为 middleware/proxy 像一个方便的全局入口。

但方便不等于稳定。只要这层逻辑绑定了某个运行时能力,它就会在目标平台不支持时变成发布门槛。问题不再是“这一段代码有没有 bug”,而是“这一段职责是否应该继续放在这里”。

/projects -> /workspace 不需要靠 proxy 证明自己

旧项目入口跳转本质上是路由关系。它不需要在请求运行时里动态判断,也不应该为了一个稳定跳转去引入部署边界风险。把它迁回 next.config.mjs 的 redirects,是更清楚的职责归位。

安全响应头也不该绑死在代理层

安全响应头同样如此。如果它们是全局、静态、可配置的,就应该优先放在 headers 配置里,而不是依赖 proxy 在运行时补上。这样做不是为了少写代码,而是为了减少“运行时兼容”这个失败面。

删除 src/proxy.ts 比兼容它更诚实

最后真正让链路稳定的,不是把 proxy 硬改成另一种写法,而是承认它不该继续存在。redirects 和 headers 回到 next.config.mjs,回归测试直接锁住:src/proxy.ts 不应再出现,关键安全头和旧路由跳转必须留在配置层。

我后来把兼容性拆成三张表

这次之后,我不再把 Cloudflare 兼容性看成一个模糊清单,而是拆成三张更实用的表。

第一张是运行时边界表。哪些逻辑默认需要 Node,哪些 API 在边缘环境里不成立,哪些依赖虽然能安装但行为假设传统服务端存在。它的作用是阻止“本地能跑所以线上也能跑”的惯性。

第二张是职责归位表。不是所有曾经放在 middleware / proxy 里的逻辑都属于 middleware / proxy。跳转、headers、route handler、页面层、server action,各自应该承担什么,要比“哪里写起来快”更重要。

第三张是失败信号表。哪些失败必须在发布前暴露,哪些失败可以留到人工验收,哪些失败一旦线上出现成本就很高。build:cf 被纳入硬检查,就是因为它对应的是最昂贵的误判:你以为已经 ready,目标平台却并不接受。

release gate 应该先拦最贵的失败

我现在更愿意让 release gate 先回答一个不那么令人愉快的问题:最贵的失败会不会被及时发现?

对这个项目来说,普通测试、lint、本地 build 当然重要,但它们不能替代目标平台链路。Cloudflare / OpenNext 的失败如果只在最后一刻才出现,就说明门槛设置得太晚。真正稳的流程不是把所有检查堆成仪式,而是把最容易误判的那一层提前。

构建脚本不是装饰,它定义了发布现实

npm run build:cf 之所以应该存在,不是为了让脚本列表看起来专业,而是为了把“目标平台到底接不接受当前结构”变成一个可重复问题。只要这一步没有进入日常验证,所有前面的绿灯都只是部分事实。

Cloudflare / OpenNext 不是坏选择。它真正要求的是你别把传统 Node 语境里的顺手写法,默认为目标平台也会接受。越早把边界写进配置、测试、脚本和文档,发布就越像工程;越晚承认这些边界,部署就越像碰运气。

这篇要补上的现场证据

这篇的现场不是抽象平台比较,而是 MakePlans 在 Next.js、Cloudflare/OpenNext、内容生成和公开路由之间反复确认边界的过程。真正影响选择的不是“Cloudflare 能不能跑 Next.js”,而是站点里哪些路径必须静态生成,哪些 API 必须保留运行时语义,哪些内容生成文件会影响 sitemap、RSS 和搜索索引。

我会先用命令把运行时边界拉出来

Cloudflare/OpenNext 适配不能靠读文档时的感觉判断。每次涉及运行时边界,我都会先把本地构建和目标构建分开跑,让差异尽早暴露。

npm run build
npm run build:cf

如果本地 build 通过但 build:cf 不通过,说明问题不在“Next.js 项目能不能构建”,而在目标平台是否接受当前结构。这个差异就是平台选择最真实的证据。

继续读

Cloud stack decisions

从部署选择,到发布检查,再到可复用资源。

这组页面围绕低成本 Cloud 栈和发布边界展开,适合按顺序阅读,也方便从文章直接跳到对应清单。