新时代的 PHP:RSC 的边界错位与工程代价
AI Summary
讽刺的是,这篇文章,也就是你看到这个网站,正在运行在 Next.js 上,但由于无法承受的问题和维护压力,我已经很认真地把“放弃 Next.js”提上日程了。
Next.js 本身我正在用、也在骂。原因不复杂:这两年最“新时代 PHP”的东西,不是某个新框架,而是 前端在不自知的情况下,被推去承接后端该承担的安全边界与运行时复杂度——而 React Server Components(RSC)正是这件事的放大器。
我会用一种比较“理性地骂”的方式,把事情讲清楚:
RSC 到底是什么;它为什么天然把边界搞得很危险;最近这次 RSC 相关的严重漏洞到底发生在什么位置;Next.js 与 Vercel 又是怎样把这些能力包装成“工程默认选项”;以及这些选择的工程代价最终转为了什么。
零、先叠甲:“新时代 PHP”到底在说什么
在正文开始前,我想先给这篇文章定个调,以免误伤友军:
- 我并不否定 Next.js 在效率上的统治力:对于独立开发者、验证型 MVP(最小可行性产品)、或者内容型网站(博客、文档),Next.js + Vercel 依然是目前地球上最快的上线组合。这种“一把梭”的爽感,我是认可的。
- “新时代 PHP”不是贬义词,而是架构描述:正如 PHP 曾凭借“请求即脚本”的低门槛撑起了半个互联网,RSC 也在试图降低全栈门槛。但我所担忧的,是它在 不经意间 复刻了当年 PHP 混合开发时期的“安全黑洞”与“耦合泥潭”。
- 我的视角偏向“严谨工程”:作为一名习惯了 Spring Boot/Java 后端架构,或者是 ktor fiber fastify,同时主要学习前端领域的开发者,我对 “边界(Boundary)” 极为敏感。这篇文章的核心冲突,在于 “前端追求极致 DX(开发体验)” 与 “后端追求极致安全与稳定” 之间的天然矛盾。
-
以前:前端是前端,后端是后端。接口契约、鉴权、审计、限流、WAF、灰度,都在后端层解决。
-
现在(RSC/Server Actions 的兴起):把“后端能力”塞进 React/Next 的文件与组件里,用
use server把函数变成“远程可调用逻辑”。官方甚至明确表达过“你可以不需要手写 API Route”。 Next.js13 的叙述
这不是“全栈”那么简单,这是 把接口面向公网的语义,无感的形式伪装成了组件内部的语义。语义伪装的后果通常只有两种:安全事故,或者工程事故——最好两者别一起来。
一、RSC 到底是什么:它不是 SSR 的升级,而是“协议 + 运行时分裂”,也就是,边界错位从协议层就开始了
如果只看铺天盖地的宣传,RSC 很容易被误解成“更快的 SSR”或“把组件放到服务器渲染”。但 React 官方对 Server Components 的定义里有个关键点:它运行在一个“与客户端应用或 SSR 服务器分离的环境”,并且输出的不只是 HTML。Server Components
更准确地说,RSC 带来三件事:
-
组件分层变成运行时分层
-
Client Components:在浏览器跑,拿到 JS bundle。
-
Server Components:在服务器跑(可能是构建时,也可能是请求时),可以直接访问数据库/文件系统等服务器资源。所以你可以看到,官方甚至搞了一个教程建议你在 RSC 写 sql,然后使用 Suspense 做流式传输。App Router: Getting Started | Next.js
-
-
传输的不是页面,而是“可恢复的组件树数据”(Flight payload)
你可以把它理解为:服务端把一棵 React 树编码为一种可流式传输的数据结构,客户端再把它解码/拼装回 React 状态,也就是本身他就和 SSR 是完全不同的。 -
组件开始携带“能力”
一旦你允许“Server 侧函数可被触发”(例如 Server Actions/Server Functions),你就不再只是渲染 UI:你是在暴露一套“从浏览器直达服务器逻辑”的通道,那这里就很危险了,因为这就从获取运行结果,变成了一种类似 RPC(远程过程调用)。Next.js
这第三点,就是边界错位的根源。
二、Flight 协议的现实:它必然涉及“反序列化”,而反序列化就必然是风险点,配合 JS 本身的设计那就无所不能了
很多人听到“反序列化漏洞”就条件反射:又是 Java 那套?
但这里不是对象流,而是 协议层为了让 React 树可恢复,必须做的解码过程。
React 的实现里,你能看到它对 payload 的处理方式:JSON.parse 并不是直接 parse 成普通 JSON,而是带了一个自定义 reviver(_fromJSON),用来把特殊标记字符串(例如 $ 开头)解释成 Promise、Server Reference、Map、Set、FormData、Date、BigInt 等结构。
例如:
ts
以及 reviver 里对 $ 前缀的分派:
ts
这些代码只是说明一个事实:
RSC 的正常工作方式,就是在服务器端“解码外部输入”并恢复出可执行/可解析的结构。
你一旦把“可触发的服务器函数”也绑在同一套通道上,安全边界就从“HTTP API”滑向“协议解码器”。
这就是架构选择的必然后果,毕竟安全总要有模块负责。
Java 的 ObjectInputStream 踩过,Python 的 Pickle 踩过,现在轮到 JS 的 Flight 了。
三、这次离谱事件的原因:不是业务逻辑,而是解码器被当成了能力网关,责任边界到了前端
不久之前,React 官方发布公告:RSC 存在一个“未认证的远程代码执行(RCE)漏洞”,可通过利用 React 解码发送到 React Server Function 端点的 payload 触发。
第三方安全团队的分析进一步强调了它的性质:这不是某个业务接口没鉴权,而是 Flight/RSC 相关的“处理链路”出现了可被利用的点,导致在默认配置下就可能被打穿。wiz.io
并且,这类问题会天然产生“生态级连坐”:
-
React 层:CVE-2025-55182(React/Flight/RSC 相关)
-
框架层:Next.js 等实现 RSC/Server Actions 的框架会一起进入受影响范围
在这个事故发生的时候我还刚好甲流,发烧很难受,但是还不得不强撑着起来更新依赖
在这次事件中,责任边界发生了变化:
以前你写后端 API,你会默认:
-
输入校验是业务层的事
-
鉴权/鉴别来源是网关/中间件/后端统一层处理的
-
序列化/反序列化是你可控的
而在 RSC/Server Actions 的组合里,很多工程的默认形态是:
-
浏览器发一个“协议包”
-
框架解码恢复结构
-
框架再把它路由到某个服务器函数
解码器变成网关,这就会让“协议实现细节”直接进入你的威胁模型。React
四、继续复盘,事情的严重:Next.js 把能力包装成默认姿势,甚至开箱即用:于是前端开始背后端的锅
于是这个时候主推 App Router 的 Next.js 出场了。
如果 Next.js 只做“传统前端 + SSR”,其实还没那么灾难。真正的拐点在于:Next.js 把 RSC/Server Actions 作为 App Router 时代的主叙事之一,并明确描述 Server Actions 是 在服务器执行的异步函数,并且可在 Server/Client Components 中使用。Next.js
那事情就变得微妙了:
你把函数写在前端项目里,但它会在服务器跑;你在组件里调用它,但它是“后端入口”。
到这里,“前端插手后端开发”的现实就出现了:
-
这条链路一旦出安全问题,你不能再说“后端会兜底”,因为后端入口就在 Next 项目里。
-
作为前端开发,你不能再说“我们只写页面”,因为你实际部署的是一段“可被触发的服务器逻辑”。
-
你甚至很难用传统后端的方式给它加网关、加 WAF、加统一鉴权层,因为框架已经替你决定了请求应该如何被解释。
Next.js 当然意识到了风险,于是也着重添加了安全相关的配置和能力。Next.js
但这件事本身看起来啊就很荒谬:你看起来在写“组件”,实际上你在配置后端的安全策略。
而现实里,因为 Nextjs 将开发后端的门槛降得太低了,很多“纯前端开发者”并不具备这套安全心智模型:他们会把它当成“更舒服的表单提交方式”,直到某天看到公告写着“Unauthenticated RCE”。React
这就是强制前端插手后端的后果,前端想把后端写成语法糖。
然后再把后端能力塞进前端默认工程,假装这只是 DX(开发体验)。
五、再说 Vercel:用 Next.js 做壳,引流平台能力,然后把复杂度外包给用户
这里我不否认 Vercel 的产品力,它确实把部署体验做得极致,并且它也提供防护体系(包括平台级防护、以及可配置 WAF/Firewall)。Vercel
但问题在于:Next.js 的很多“卖点”,在工程上并不是“框架能力”,而是“平台叠加能力”。
举两个典型例子:
1)ISR:听起来是框架特性,实际是缓存/失效/函数编排的一整套系统
Next.js 文档会告诉你如何做 ISR、如何触发再生成、如何 revalidate。Next.js
Vercel 的文档则会进一步解释 ISR 在其平台上会如何落到具体的 Function/缓存与配置上。Vercel
工程现实是:
-
你以为你在用“Next.js 特性”;
-
你实际在绑定“某个平台对缓存失效与边缘分发的具体实现”。
这不是不能用,而是你应该清楚:这类特性会把你从“框架迁移”推到“平台迁移”。
2)“中间件”这个词本身就带歧义:它并不在你的服务中间,而是在 Edge Runtime
Next.js 早期版本 Middleware 只支持 Edge runtime,不能用 Node.js runtime。Next.js
而 Edge Runtime 又意味着:你拿到的是 Web API 语义、受限的运行环境,与 Node.js 的能力集不同。Next.js
所以很多时候你写 Middleware 的体验是:
-
名字叫“中间件”,你以为它像后端的 middleware/filter;
-
实际它更像“边缘网关脚本”,能力受限、调试困难、行为受运行位置影响。
把它叫 middleware,不是错,但它会天然误导大量开发者建立错误心智模型:以为自己在写“后端中间件”,其实是在写“边缘逻辑”。
这样,你以为它值得信赖,然而实际上只是展示他们平台能力的一个手段。
说的更直白些,你以为你写的是标准 Web 代码,实际上你写的是 Vercel 专用的 DSL。
六、工程代价清单:不是学一下就会,而是你要多维护一个世界观
到这里,骂点就不应该停留在“Vercel 坏”“Next 坏”“RSC 坏”,而是要落到工程代价——因为这才是你会长期为之付费的部分。
1)运行时分裂:Node、Edge、Client、Server Component 环境并存
Next.js 自己就把 runtime 拆成 Node 与 Edge 两套,并说明能力差异。Next.js
而 RSC 又把组件分成 Server/Client 两类。React
你最终维护的是一个矩阵:
作为全栈框架的使用者,它只是事实:你需要为每个格子建立正确预期,否则问题会以“看起来像 bug”的方式出现。
2)安全边界内陷:从“API 网关/后端鉴权”变成“框架配置 + 协议解码链路”
这次事件已经告诉我们:攻击面不止是业务 API,而是 RSC/Flight/Server Functions 的解码链路。React
Nextjs 成功证明了 全栈不难,难的是把责任边界划清楚。
3)平台耦合:迁移成本从“换框架”上升到“换平台能力组合”
ISR、Edge、WAF、以及各种“默认最佳实践”,在 Vercel 上确实丝滑。Vercel
但当你想把它搬走(自建、换云、甚至只是加一层代理),你就会发现:你迁移的不只是代码,还有一堆隐含假设。
七、最后补一刀:AI 生成代码默认 Next.js 起手,是在扩大这种错位
现在很多 AI 生成前端项目的默认姿势,基本都是:Next.js + App Router + Server Actions + Shadcn/ui + 一堆平台最佳实践模板,拿过来一把梭哈。Vercel 自己也在把生成 UI/生成代码与 Next.js 工作流绑定( v0 就是他们家的代表,额不是 VoidZero 那个 v0,是 v0.dev)。
问题不是 AI 生成了 Next.js,而是:
-
AI 会把“默认最流行模板”当成“默认最正确架构”
-
它很少提醒你:我创建的是全栈项目,这里的 Server Actions 是后端入口、这里的 Middleware 跑在 Edge、这里的 ISR 会平台耦合、这里的安全边界需要你自己负责
-
于是新手更容易在不理解 RSC 的情况下,把项目推到全栈默认形态
当理解成本被隐藏,风险成本就会延后爆炸。然后大家再回来补课:什么是 Flight、什么是 reviver、什么是来源校验、什么是边缘运行时差异——这就是工程代价,一个激进框架背后的,一个把后端当前端语法糖来写的工程代价。
总结:我为什么说这是“新时代 PHP”
我不反对 RSC,也不否认它在某些场景能带来性能与工程收益。我反对的是:
-
把后端能力伪装成组件语法糖,让不具备后端工程化能力的人去写后端入口。
-
把协议解码链路推成事实网关,让安全问题从业务层上移到框架实现细节。
-
用平台体验裹挟框架路线,让特性越来越像为平台服务,迁移越来越像掏空重来。
之所以人们在放弃 Next.js,不是因为它写不了什么,恰恰相反他有面面俱到的能力;而是因为我不想再把一个前端工程,硬凑成一个后端工程,然后在漏洞公告出现时才意识到:原来我部署的不是页面,是一段可被触发的服务器逻辑。
写这篇文章,并非要号召大家明天就卸载 Next.js 回去写 JSP。 技术的螺旋上升总伴随着代价。React 团队探索 RSC 的勇气值得敬佩,Vercel 想要统一 Web 开发流的野心也令人惊叹。但在铺天盖地的营销叙事中,我们作为一线工程师,必须保持 冷眼旁观 的能力。
我们不能因为 Vercel 演示里的 Demo 很炫酷,就忽略了背后那个正在泄漏数据库连接的 Lambda;也不能因为 use server 写起来很爽,就忘记了此时此刻你正在裸奔。 理性地评判,是为了在它把我们的工程搞崩之前,先搞清楚底线在哪里。


