Technical Deep Dive: How React Server Components Work and Where the Vulnerabilities Appear
Source: Dev.to
A simplified but accurate RSC request lifecycle looks like this:
-
Incoming request reaches the RSC endpoint
- In Next.js, this typically maps to an internal route such as
/_rsc. - The request is processed before authentication and before application middleware.
- In Next.js, this typically maps to an internal route such as
-
Request body parsing and protocol decoding
- The payload is parsed according to the React Flight protocol.
- This includes decoding serialized component references, props, and execution hints.
- At this stage, React assumes the payload shape is valid enough to be processed.
-
Component graph reconstruction
- React reconstructs the Server Component tree from the serialized input.
- Module references are resolved and loaded.
- Async boundaries and Suspense segments are registered.
-
Server rendering and streaming
- Components are executed on the server.
- Output is serialized again and streamed back to the client in chunks.
- Error boundaries and partial results are flushed incrementally.
-
Error and recovery
- If an exception occurs mid‑stream, React attempts to recover gracefully.
- Metadata, stack traces, and module identifiers are still available internally.
- Each of these phases is sensitive to malformed or adversarial input.
How Denial of Service Is Triggered
The DoS issue arises primarily in steps 2 and 3, before any application logic is involved.
A malicious request can:
- Inflate the serialized payload size.
- Introduce deeply nested or cyclic references.
- Force excessive async boundary resolution.
Conceptual example (JSON payload):
POST /_rsc
Content-Type: application/json
{
"type": "ServerComponent",
"props": {
"children": {
"children": {
"children": {
"...": "..."
}
}
}
}
}
Even if the payload is syntactically valid, React will:
- Attempt to deserialize it.
- Build an in‑memory component graph.
- Schedule execution and streaming work.
This leads to:
- High CPU usage during decoding.
- Memory pressure during graph reconstruction.
- Worker or event‑loop saturation.
Crucially, this happens without hitting user‑defined code and without authentication, making traditional defensive assumptions at the application layer largely ineffective.
How Source Code Exposure Can Occur
Source code exposure is a side effect of how React handles errors during steps 4 and 5.
When a rendering or serialization error occurs mid‑stream:
- React still holds references to internal module paths.
- Stack traces include resolved file locations.
- Component metadata is partially serialized.
In certain edge cases, this information may:
- Leak into the streamed response.
- Appear in error payloads.
- Be flushed before the stream is properly aborted.
Example of leaked information (JavaScript error):
Error: Failed to resolve Server Component
at /app/src/components/admin/UserList.server.tsx
at react-server-dom-webpack/server
This reveals:
- Project directory structure.
- Server‑only component boundaries.
- Internal module layout and naming.
While not a standalone exploit, this materially reduces the effort required for targeted attacks.
Why Server Actions Are Not Required
A common misconception is that these risks only apply when using Server Actions. In reality:
- Server Actions are an API surface.
- RSC is a runtime and protocol.
Even a simple page like:
export default async function Page() {
return ;
}
still triggers:
- Request deserialization.
- Component graph reconstruction.
- Server execution and streaming.
The vulnerable logic executes regardless of whether Server Actions are defined.
Key Takeaways
- React Server Components introduce a fundamentally new server‑side execution model that combines request deserialization, component graph reconstruction, and streaming‑based rendering into a single framework‑managed pipeline.
- The effective attack surface exists before authentication, before application middleware, and before any user‑defined business logic is executed.
- Malformed or adversarial RSC requests can lead to:
- Denial of Service through excessive CPU usage, memory pressure, and worker or event‑loop saturation during deserialization and component graph reconstruction.
- Accidental disclosure of server‑side implementation details, including internal file paths, component boundaries, and module layout, as a side effect of streaming error handling.
- These issues are not caused by misconfigured applications or unsafe user code; they are framework‑level vulnerabilities in the React Server Components runtime itself and cannot be mitigated reliably through application‑layer defenses alone.