React2Shell earned a CVSS score of 10.0. Exploitation requires no authentication, no user interaction, and no custom code. A project scaffolded with create-next-app, built for production exactly as documented, was remotely exploitable the moment it deployed. This was not a misconfiguration. It was the default.
The question that follows is: how did default become vulnerable? This article examines one dimension of the React Server Components security landscape: the trust model that made the default exploitable. The answer sits in the React Server Components trust model, a design decision that assumed Flight payloads were trustworthy, and then deployed that assumption to every App Router application. By the end of this analysis, you will have a framework for evaluating whether your own architecture assumes trust where it shouldn’t.
Why Does React2Shell Work on Default Next.js Configurations with No Custom Server Code?
React2Shell exploits default Next.js App Router installations because the app/ directory structure with page.tsx files automatically participates in the Flight protocol. No eval(), no custom child_process usage, no configuration flag is required to activate the deserialisation path. Testing by Wiz Research indicates near-100% exploitation reliability, and their data shows 39% of cloud environments contain vulnerable instances. The affected range covers React 19.0 through 19.2 (patched in 19.0.1, 19.1.2, and 19.2.1), plus Next.js 15.x, 16.x, and canary builds from 14.3.0-canary.77.
Every App Router deployment exposes RSC endpoints: /_rsc, /__flight, /_react, and Server Action POST endpoints. These accept and deserialise Flight payloads from HTTP requests. The server-side deserialiser inherited the payloads-are-trusted assumption from a protocol originally designed for server-to-client communication. When the Next.js App Router made the server also a receiver of client-sent Flight payloads, nobody re-evaluated whether those payloads should be treated as hostile.
The exploit chain is deterministic logic abuse, not memory corruption. Attackers craft fake Chunk objects with custom then methods in the Flight payload. React’s deserialiser resolves them as Promises, and the attacker-controlled then handler hijacks the internal _response object to achieve arbitrary code execution. OffSec characterised it as pure logic abuse built on expected JavaScript features like promise resolution and nested deserialisation.
Server Functions and Server Actions infrastructure is auto-exposed on every App Router deployment, even when no "use server" directives exist in your code. The Pages Router is not affected because it does not participate in the Flight protocol. If your application runs on the Pages Router with no App Router routes, you are not vulnerable to CVE-2025-55182. That architectural distinction is why the vulnerability caught so many teams off guard: the exploit path was introduced when Next.js made the App Router the documented default. For the full technical walkthrough — from crafted HTTP request to remote code execution — see the React2Shell exploit mechanism in detail.
How Does the RSC Trust Model Compare to Traditional Server-Side Rendering Security?
In traditional SSR (Express plus EJS, Rails ERB, Django templates), the server renders HTML from trusted templates. Untrusted user inputs pass through explicit escaping boundaries. The attack surface is the template rendering engine’s handling of user-supplied data. Every input is untrusted by convention, and the framework’s job is to prevent that untrusted data from becoming executable.
In RSC, the attack surface is the Flight deserialiser itself. The user input is the entire serialised component tree, a richer attack surface containing function references, module references, Promise-like objects, and cyclic data structures. All of it is treated as trusted by default. As Kodem Security noted, React’s server runtime was never built to handle untrusted input.
The REST API comparison sharpens the distinction. REST enforces an explicit security boundary where every request parameter, header, and body is untrusted by convention and validated before processing. RSC’s deserialisation-first model means exploitation occurs before any application-level validation can execute, as the RCE occurs before any routing logic or auth gates. Where REST limits its attack surface to the narrow set of validated inputs reaching application logic, RSC exposes the Flight deserialiser’s entire object graph reconstruction, handling arbitrary object shapes, prototype chains, and thenable resolution before the application ever sees the data.
The Flight protocol’s wire format is a hybrid row-based streaming format designed to resolve component trees incrementally with references and module imports. JSON, by contrast, is deliberately limited: it carries no execution context, no code, no object methods. That limitation is a security feature, and it is one RSC does not share.
Is the RSC Security Model Fundamentally Different, or Is This a Maturity Problem?
This is the question that divides security researchers, and the honest answer is that the evidence supports both interpretations.
The maturity argument goes like this: the Flight protocol is a young serialisation format compared to JSON or Protocol Buffers. It was designed for a trusted server-to-client communication space. Its deserialiser was not battle-tested against adversarial inputs, and the recurring CVE pattern (CVE-2025-55182 for RCE, CVE-2025-55184 for DoS, CVE-2025-55183 for source code exposure, and CVE-2025-67779, an incomplete fix) represents growing pains. The initial patch for CVE-2025-55184 was bypassed within days, leading to a second CVE. This looks like a protocol hardening under fire. The patch frequency across React 19.x supports this reading.
The fundamental argument says any serialisation format rich enough to represent executable code patterns (thenables, prototype chains, cyclic references, function markers $F, module references $L) will always carry an attack surface when exposed to untrusted input, regardless of maturity. The CVE diversity supports this: RCE, DoS, information disclosure, all from the same deserialisation boundary. Java’s ObjectInputStream vulnerabilities persisted across years of patching because the deserialisation model was permissive at the architectural level. Python’s pickle documentation explicitly warns against unpickling untrusted data, yet the warning is routinely ignored in practice. PHP’s unserialize() created gadget chains where legitimate classes from widely used libraries became attack vectors. You cannot fix this by “not using dangerous classes” because your dependencies contain the gadgets.
So the patch frequency supports maturity, while the CVE diversity supports fundamental. Whichever interpretation you favour, both demand the same defensive posture: adding validation boundaries that treat Flight payloads as untrusted by design. Those measures (schema enforcement, process isolation, input sanitisation) are architecturally sound regardless. Even the maturity reading implies a multi-year hardening process during which adopters bear the risk, and as DebugBear noted, security is the biggest challenge facing RSCs today.
How Do Other React Frameworks Handle the Server-Client Trust Boundary Differently?
The vulnerability follows the Flight protocol implementation, not the framework brand. Understanding how different frameworks handle the trust boundary gives you a concrete sense of the options available.
Next.js App Router enables RSC and Flight protocol endpoints by default, with Server Action infrastructure auto-exposed on every deployment. This is the maximum-default-exposure model. Remix (acquired by Shopify) uses loaders and actions with explicit data boundaries: server functions return plain JSON objects, not serialised component trees, and there is no Flight protocol deserialisation step on the server. The trust boundary is explicit because every response is treated as application data, not executable instructions.
TanStack Start provides server functions with a serialisation model that avoids reconstructing arbitrary object graphs from client input in the manner of the Flight protocol. The architectural choice to avoid Flight’s expressive format reduces the deserialisation attack surface. Waku, a minimal RSC framework, bundles the same react-server-dom-* packages as Next.js and was explicitly affected by CVE-2025-55182, demonstrating that minimalism offers no inherent protection against protocol-level trust-model flaws. Traditional React SPAs without RSC have no server-side deserialisation attack surface at all: the server sends static assets and API responses, and the attack surface is the API layer, which follows the REST validation-before-processing model.
The comparison reveals a spectrum: minimal attack surface (traditional SPA with REST API), moderate (Remix or TanStack with explicit data boundaries), maximum (Next.js App Router or Waku with Flight protocol deserialisation). Teams evaluating framework migrations should understand where their candidate framework sits on this spectrum. The CVE-2025-66478 and CVE-2025-55182 duplicate relationship, where the Next.js advisory was later rejected as a duplicate of the primary React CVE, illustrates that the vulnerability flows from upstream, not from any single downstream framework.
Which brings us to the practical question: if you are evaluating an RSC framework, what should you be asking its vendor before adopting server components?
What Should You Ask Your Framework Vendor About RSC Security Before Adopting Server Components?
These are not prescriptive checklist items. They are the analytical dimensions you should probe when evaluating an RSC framework vendor, with the React2Shell evidence as motivating context.
The first dimension is the deserialiser input validation boundary. Does the vendor’s RSC implementation assume Flight payloads are trusted, or does it enforce structural validation before deserialisation begins? React2Shell succeeded because the deserialiser treated client-supplied Chunk objects as legitimate. The React team’s fix required tightening validation and eliminating code paths that allowed untrusted instructions to survive deserialisation. Use that as your benchmark for what vendor hardening should include: validation guardrails around function markers, module references, and Promise resolution paths.
Second is the security advisory process and patch latency. What is the vendor’s CVE disclosure cadence, coordinated release process, and typical time-to-patch? Vercel‘s coordinated release in May 2026 demonstrates the operational cost framework vendors absorb, and the standard is worth comparing.
Process isolation forms the third dimension. How does the vendor isolate server-side RSC execution from the host environment? Look for process-level isolation, capability dropping, and whether the default deployment model limits post-exploitation blast radius. Vercel-hosted apps benefit from platform-level request filtering, but platform protections reduce blast radius, they do not close the deserialisation vulnerability.
The fourth dimension is the Flight protocol dependency chain. Does the vendor maintain their own fork of react-server-dom-webpack or react-server-dom-turbopack, or track upstream React directly? Understanding who owns the deserialisation implementation determines where patches originate and how quickly they propagate.
Fifth, and perhaps most revealing: is hardening treated as ongoing investment or reactive patching? A vendor answering “reactive” implicitly expects you to absorb discovery-to-patch risk. The maturity-versus-fundamental debate from the previous section makes this question essential, because the patch frequency and CVE diversity suggest the deserialisation surface still holds undiscovered issues.
For your own deployment, assessing exposure means asking whether you use App Router with server components (exposed), whether your WAF inspects Flight payloads (mitigating), whether your runtime provides process isolation (reduces blast radius), and whether you run patched versions (remediated). The prioritisation case is straightforward. GreyNoise recorded over 1.4 million exploitation attempts in a single seven-day observation period. If your application processes untrusted Flight payloads and you have not patched, this vulnerability ranks above other issues in your backlog because exploitation is deterministic, unauthenticated, and actively occurring.
React2Shell was patched. The architectural question it surfaced remains open — and how the exploitation evidence shapes the accountability question is something every team adopting RSC must confront. The Flight protocol’s serialisation format carries executable patterns that traditional web frameworks avoid by design, and exposing that format to untrusted client input creates an attack surface whose boundaries are still being mapped. Real-world exploitation data validates the trust-model analysis: attackers understood the trust boundary weakness and exploited it at scale. Teams adopting RSC are not just adopting a rendering strategy. They are adopting a trust model whose hardening is still underway, and the gap between default and secure is one attackers are exploiting right now.
Frequently Asked Questions
Does the Pages Router protect me from React2Shell?
Yes. The Pages Router does not participate in the Flight protocol and has no server-side deserialisation of client-sent component payloads. The attack surface exists exclusively in the App Router’s RSC architecture. If your entire application runs on the Pages Router with no App Router routes, you are not vulnerable to CVE-2025-55182. This architectural distinction is why the vulnerability caught so many teams off guard: the exploit path was introduced silently when Next.js made the App Router the documented default.
Can I simply disable Server Actions if I am not using “use server” directives?
No, and this is a common misconception. Server Actions infrastructure is auto-exposed on every App Router deployment regardless of whether you have authored any "use server" functions. The Flight protocol endpoints that accept and deserialise client payloads are active by default. There is no configuration flag to selectively disable the deserialisation path while keeping the App Router running. Patching is the only reliable mitigation for this attack surface.
How would I know if my deployment was already compromised?
Look for unusual child processes spawned by the Node.js runtime, unexpected outbound network connections from your server, and new files written to directories like /tmp or the application root. The publicly documented post-exploitation payloads deployed XMRig cryptominers, Cobalt Strike beacons, and several RAT variants (KSwapDoor, EtherRAT, Noodle RAT). Check your process tree and network logs for these indicators. Note that skilled attackers may have cleaned forensic artefacts, so patching alone does not substitute for a compromise assessment if you ran an unpatched version during the exposure window.
Does hosting on Vercel rather than self-hosting change my exposure?
Vercel’s infrastructure adds some mitigating factors (containerised isolation, limited filesystem write access, and their own edge network inspection) but the vulnerability itself exists in your application code regardless of hosting platform. Vercel cannot prevent the Flight deserialiser in your application from processing a crafted payload. The CVSS 10.0 score reflects that the exploit path exists at the application layer. Platform-level protections reduce post-exploitation blast radius; they do not close the deserialisation vulnerability.
Is the Edge Runtime a safe alternative to the Node.js runtime for RSC?
The Edge Runtime is not affected by CVE-2025-55182 because it does not participate in the Flight protocol in the same way as the Node.js runtime. However, this is not a general security guarantee. The Edge Runtime has a different capability profile (no filesystem access, restricted Node.js APIs) that limits what an attacker can do after exploitation, but it runs the same React Server Components infrastructure. Treating the Edge Runtime as inherently safe misunderstands the problem: the vulnerability follows the Flight protocol implementation, not the runtime environment.
Does the “use server” directive give developers a false sense of security?
Yes, and this is one of the more significant implications of React2Shell. The "use server" directive implies a security boundary: code marked this way runs on the server and cannot leak to the client. But the directive is a build-time instruction to the bundler, not a runtime security enforcement mechanism. It does nothing to validate or constrain what the server-side deserialiser accepts from incoming Flight payloads. The naming convention creates an intuition about security that the implementation does not deliver, which is a documentation and developer-experience problem the React team should address directly.
What exactly did the React team change to fix CVE-2025-55182?
The patch added validation guards around the Flight deserialiser’s handling of Chunk objects, specifically preventing attacker-controlled then methods from hijacking the internal _response object during Promise resolution. The fix introduces a runtime check that rejects Chunk types from client payloads that should only originate from server-side serialisation. This is a targeted hardening of the deserialisation path rather than a fundamental protocol redesign. It closes this specific exploit chain but does not eliminate the broader attack surface associated with deserialising expressive payloads from untrusted clients.
Does this vulnerability affect client-side React applications at all?
No. React2Shell is exclusively a server-side vulnerability. The exploit targets the Flight deserialiser running on the server, not the React runtime in the browser. Client-side React applications, including those built with Create React App or Vite, have no Flight protocol endpoints and no server-side deserialisation step to exploit. The confusion arises because “React” is the branding, but the vulnerability lives in react-server-dom-webpack and react-server-dom-turbopack, packages that only exist in server-side RSC deployments.
If I cannot patch immediately, can I block this at the network layer?
A WAF rule blocking requests to known Flight protocol endpoint paths (/_rsc, /__flight, /_react) and POST requests with Flight-specific content types can provide partial mitigation. However, this approach has two meaningful limitations: it may break legitimate application functionality, and determined attackers can potentially route payloads through alternative paths. Network-layer blocking is a temporary stopgap, not a substitute for patching. The deterministic nature of the exploit means every unauthenticated request to a Flight endpoint on an unpatched server represents a successful compromise.
Is this the only known RCE in React Server Components, or should we expect more?
CVE-2025-55182 is the most severe RSC vulnerability disclosed to date, but the recurring deserialisation pattern across React 19.x (including prior DoS vulnerabilities) strongly suggests that additional issues will surface as the protocol receives more adversarial scrutiny. The Flight deserialiser was designed for a trusted communication channel and is now being stress-tested against the reality that client-sent payloads are attacker-controlled. Security researchers and the React team are actively auditing the deserialisation surface. Teams running RSC in production should assume further CVEs will emerge and plan their patch response cadence accordingly.