新时代的 PHP:RSC 的边界错位与工程代价

grtsinry43
12/14/2025(更新于 12/14/2025
12 views
预计阅读时长 19 分钟

AI Summary

DEEPSEEK-R1

讽刺的是,这篇文章,也就是你看到这个网站,正在运行在 Next.js 上,但由于无法承受的问题和维护压力,我已经很认真地把“放弃 Next.js”提上日程了。

Next.js 本身我正在用、也在骂。原因不复杂:这两年最“新时代 PHP”的东西,不是某个新框架,而是 前端在不自知的情况下,被推去承接后端该承担的安全边界与运行时复杂度——而 React Server Components(RSC)正是这件事的放大器。

我会用一种比较“理性地骂”的方式,把事情讲清楚:

RSC 到底是什么;它为什么天然把边界搞得很危险;最近这次 RSC 相关的严重漏洞到底发生在什么位置;Next.js 与 Vercel 又是怎样把这些能力包装成“工程默认选项”;以及这些选择的工程代价最终转为了什么。


零、先叠甲:“新时代 PHP”到底在说什么

在正文开始前,我想先给这篇文章定个调,以免误伤友军:

  1. 我并不否定 Next.js 在效率上的统治力:对于独立开发者、验证型 MVP(最小可行性产品)、或者内容型网站(博客、文档),Next.js + Vercel 依然是目前地球上最快的上线组合。这种“一把梭”的爽感,我是认可的。
  2. “新时代 PHP”不是贬义词,而是架构描述:正如 PHP 曾凭借“请求即脚本”的低门槛撑起了半个互联网,RSC 也在试图降低全栈门槛。但我所担忧的,是它在 不经意间 复刻了当年 PHP 混合开发时期的“安全黑洞”与“耦合泥潭”。
  3. 我的视角偏向“严谨工程”:作为一名习惯了 Spring Boot/Java 后端架构,或者是 ktor fiber fastify,同时主要学习前端领域的开发者,我对 “边界(Boundary)” 极为敏感。这篇文章的核心冲突,在于 “前端追求极致 DX(开发体验)”“后端追求极致安全与稳定” 之间的天然矛盾。

这不是“全栈”那么简单,这是 把接口面向公网的语义,无感的形式伪装成了组件内部的语义。语义伪装的后果通常只有两种:安全事故,或者工程事故——最好两者别一起来。

一、RSC 到底是什么:它不是 SSR 的升级,而是“协议 + 运行时分裂”,也就是,边界错位从协议层就开始了

如果只看铺天盖地的宣传,RSC 很容易被误解成“更快的 SSR”或“把组件放到服务器渲染”。但 React 官方对 Server Components 的定义里有个关键点:它运行在一个“与客户端应用或 SSR 服务器分离的环境”,并且输出的不只是 HTML。Server Components

更准确地说,RSC 带来三件事:

  1. 组件分层变成运行时分层

  2. 传输的不是页面,而是“可恢复的组件树数据”(Flight payload)
    你可以把它理解为:服务端把一棵 React 树编码为一种可流式传输的数据结构,客户端再把它解码/拼装回 React 状态,也就是本身他就和 SSR 是完全不同的。

  3. 组件开始携带“能力”
    一旦你允许“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 等结构。

https://github.com/facebook/react/blob/323b6e98a76fe6ee721f10d327a9a682334d1a97/packages/react-server/src/ReactFlightReplyServer.js

例如:

ts
1const value = JSON.parse(chunk.value, chunk._response._fromJSON);

以及 reviver 里对 $ 前缀的分派:

ts
1if (value[0] === '$') { 
2// Promise / Server Reference / Map / Set / FormData / Date / BigInt ... 
3}

这些代码只是说明一个事实:

RSC 的正常工作方式,就是在服务器端“解码外部输入”并恢复出可执行/可解析的结构。
你一旦把“可触发的服务器函数”也绑在同一套通道上,安全边界就从“HTTP API”滑向“协议解码器”。

这就是架构选择的必然后果,毕竟安全总要有模块负责。

Java 的 ObjectInputStream 踩过,Python 的 Pickle 踩过,现在轮到 JS 的 Flight 了。


三、这次离谱事件的原因:不是业务逻辑,而是解码器被当成了能力网关,责任边界到了前端


不久之前,React 官方发布公告:RSC 存在一个“未认证的远程代码执行(RCE)漏洞”,可通过利用 React 解码发送到 React Server Function 端点的 payload 触发。

React

第三方安全团队的分析进一步强调了它的性质:这不是某个业务接口没鉴权,而是 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

你最终维护的是一个矩阵:

写法运行位置API 能力风险点
Client Component浏览器浏览器 APIXSS/依赖链
Server Component服务器/构建服务器资源数据泄露/边界混乱
MiddlewareEdgeWeb API 子集行为不一致/调试困难
Server Action服务器后端入口鉴权/来源/反序列化

作为全栈框架的使用者,它只是事实:你需要为每个格子建立正确预期,否则问题会以“看起来像 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,也不否认它在某些场景能带来性能与工程收益。我反对的是:

  1. 把后端能力伪装成组件语法糖,让不具备后端工程化能力的人去写后端入口。

  2. 把协议解码链路推成事实网关,让安全问题从业务层上移到框架实现细节。

  3. 用平台体验裹挟框架路线,让特性越来越像为平台服务,迁移越来越像掏空重来。

之所以人们在放弃 Next.js,不是因为它写不了什么,恰恰相反他有面面俱到的能力;而是因为我不想再把一个前端工程,硬凑成一个后端工程,然后在漏洞公告出现时才意识到:原来我部署的不是页面,是一段可被触发的服务器逻辑。

写这篇文章,并非要号召大家明天就卸载 Next.js 回去写 JSP。 技术的螺旋上升总伴随着代价。React 团队探索 RSC 的勇气值得敬佩,Vercel 想要统一 Web 开发流的野心也令人惊叹。但在铺天盖地的营销叙事中,我们作为一线工程师,必须保持 冷眼旁观 的能力。

我们不能因为 Vercel 演示里的 Demo 很炫酷,就忽略了背后那个正在泄漏数据库连接的 Lambda;也不能因为 use server 写起来很爽,就忘记了此时此刻你正在裸奔。 理性地评判,是为了在它把我们的工程搞崩之前,先搞清楚底线在哪里。

相关推荐

六月初至七月中旬|前端学习简要总结,生活的小回顾

在这段时间里我通过完成多个项目,深入学习了前端和全栈开发的技术。主要使用了 Vue3、C++ W...

grtsinry43
7/12/2024
225
1
1

学习分享|跨域解决、安卓开发探索、油猴脚本探索

最近学习的一些内容,包括跨域问题及其解决方案,安卓开发的简单探索,OpenAI的api做了个小插...

grtsinry43
6/10/2024
250
4
3
用一个月的时间写一个自己的博客系统——Grtblog的技术介绍

用一个月的时间写一个自己的博客系统——Grtblog的技术介绍

终于,历时一个多月的开发 ~~bug~~ 和测试,这个目前问题很多很不成熟很难用的系统终于上线了.....

grtsinry43
12/14/2024
1778
9
11
PureFlow 简析:用 Kotlin 跨平台构建 RSS 阅读器

PureFlow 简析:用 Kotlin 跨平台构建 RSS 阅读器

之前研究那么久 KMP...但是直到现在也没写什么 demo 项目,这次因为身体原因刚好出不去,唉,...

grtsinry43
9/22/2025
588
0
1
学习分享|Vue3项目中使用微信SDK开发微信网页

学习分享|Vue3项目中使用微信SDK开发微信网页

最近在开发一个微信 H5 的项目,采用的是 Vue3+FastAPI,正好学习下微信用户登录,a...

grtsinry43
7/3/2024
4662
2
2

发表评论

在这里畅所欲言吧!
支持 Markdown 语法0 / 3000

网站运行时间

0
0
0
0

在风雨飘摇之中

感谢陪伴与支持

愿我们不负热爱,继续前行