CVE-2026-23869 Denial of Service in React Server Components
A high-severity Denial of Service vulnerability in React 19 is allowing attackers to fully lock up server CPU without any authentication. This vulnerability is actively exploited in the wild and poses a direct threat to any system running React Server Components.
What is CVE-2026-23869?
CVE-2026-23869 is a HIGH severity vulnerability (CVSS 7.5) affecting React 19 applications that use Server Actions. It is the third issue in a related chain — prior patches from December 2025 and January 2026 did not fully resolve the root cause. This vulnerability is actively exploited in the wild. No authentication or special skills are required; a single crafted request is enough to bring the server to a complete halt.
| CVE ID | CVE-2026-23869 |
| CVSS 3.1 | AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-400 · CWE-502 |
| Reporter | Facebook, Inc. (CNA) |
| Advisory | GHSA-479c-33wc-g2pg |
| Related CVEs | CVE-2026-23864 / CVE-2025-55182 CVE-2025-55184 |
| Package | Vulnerable | Patched |
|---|---|---|
| react-server-dom-webpack react-server-dom-parcel react-server-dom-turbopack |
19.0.0–19.0.4 19.1.0–19.1.5 19.2.0–19.2.4 |
19.0.5 19.1.6 19.2.5 |
React 18.x and below · Client-side SPA only · Apps without Server Actions · Static site generators
Three Attempts, Still Not Patched
How the System Paralysis Occurs
React Server Components rely on the Flight protocol to transmit data between client and server. When a user triggers a Server Action, the request is sent as multipart/form-data and the server deserializes the payload via the reviveModel() function.
The flaw arises because React does not validate whether keys in the incoming payload are own properties or inherited prototype properties. Attackers exploit this gap by injecting circular references and performing prototype traversal to confuse the deserializer. Rather than processing normal tasks, the server burns through CPU resolving an endless chain of malicious calls, causing an immediate and complete hang.
Traditional DoS requires massive traffic volume. This vulnerability needs only a handful of crafted requests — each one locks the CPU for 60 seconds. On a 4-core server, just 4 concurrent requests are enough to cause a complete outage.
Real-World Impact
The Anatomy of a React DoS Attack
$@0) and prototype traversal via (__proto__:then).reviveModel() begins deserializing the payload without any input checks.$@0 and calls .then(), but .then() resolves back to the same object — triggering an infinite loop.4 WAF Bypass Techniques Used by Attackers
These techniques have been observed in real-world exploitation attempts. This is why WAF defenses must be layered rather than relying on a single pattern.
charset=shift_jis in the Content-Type header causes the WAF to misinterpret the encoding while Next.js processes it normally, breaking signature matching.\u00xx to encode special characters. WAFs that skip Unicode normalization before pattern matching will fail to detect the payload.Regex Patterns to Deploy
| Pattern | ||
|---|---|---|
| Header Patterns — Prerequisite Conditions | Type | Role |
(?i)(?:next-action|rsc-action-id) | HEADER KEY | Identifies RSC requests — prerequisite trigger |
(?i)text/x-component | ACCEPT/CT | RSC content type |
(?i)next-router-state-tree | OPTIONAL | Optional header, combine to improve accuracy |
(?i)React2ShellScanner|Nuclei.*CVE | BLOCK | Known scanners actively probing for this CVE |
| Body Patterns — Malicious Payload Signatures | Severity | Description |
\$\@\d+ | CRITICAL | Self-reference signature — clearest indicator of a circular reference attack |
(?i)"status"\s*:\s*"resolved_model" | CRITICAL | Fake Chunk object spoofing internal React state |
(?i):constructor["'] | CRITICAL | Accessing Function constructor via prototype chain |
(?i)_response["']\s*: | HIGH | Response object hijacking |
(?i)_formData["']\s*: | HIGH | Form data hijacking |
(?i)__proto__|prototype\s*: | CRITICAL | Prototype pollution attack |
(?i)charset\s*= | HIGH | Charset bypass technique |
\\u00 | HIGH | Unicode escape evasion |
Platform-Specific Rule Configurations
Always test in COUNT/LOG mode first before switching to BLOCK.
Compatible: ModSecurity 2.x / 3.x (Apache, Nginx, IIS).
# ═══════════════════════════════════════════════════════════════ # ModSecurity Rules — CVE-2026-23869 / React Server Components DoS # Compatible: ModSecurity 2.x / 3.x (Apache, Nginx, IIS) # ═══════════════════════════════════════════════════════════════ # --- Rule 1: Detect RSC request headers --- SecRule REQUEST_METHOD "POST" \ "id:900001, phase:1, pass, nolog, chain" SecRule REQUEST_HEADERS_NAMES "(?i)(?:next-action|rsc-action-id)" \ "setvar:tx.rsc_request=1" # --- Rule 2: Block $@ self-reference (CRITICAL) --- SecRule TX:rsc_request "@eq 1" \ "id:900002, phase:2, deny, status:403, \ log, msg:'RSC Flight self-reference attack ($@)', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "\$\@\d+" \ "t:urlDecodeUni,t:jsDecode,t:utf8toUnicode" # --- Rule 3: Block exploit payload signatures --- SecRule TX:rsc_request "@eq 1" \ "id:900003, phase:2, deny, status:403, \ log, msg:'RSC exploit payload detected', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY \ "(?i)(?:resolved_model[\"']|:constructor[\"']|_response[\"']\s*:|_formData[\"']\s*:)" \ "t:urlDecodeUni,t:jsDecode,t:utf8toUnicode" # --- Rule 4: Block prototype pollution --- SecRule TX:rsc_request "@eq 1" \ "id:900004, phase:2, deny, status:403, \ log, msg:'RSC prototype pollution attempt', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "(?i)(?:__proto__|prototype\s*:)" \ "t:urlDecodeUni,t:jsDecode" # --- Rule 5: Block charset manipulation bypass --- SecRule TX:rsc_request "@eq 1" \ "id:900005, phase:2, deny, status:403, \ log, msg:'RSC charset bypass attempt', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "(?i)charset\s*=" \ "t:urlDecodeUni" # --- Rule 6: Block unicode escape bypass --- SecRule TX:rsc_request "@eq 1" \ "id:900006, phase:2, deny, status:403, \ log, msg:'RSC unicode escape evasion', \ tag:'CVE-2026-23869', severity:2, chain" SecRule REQUEST_BODY "\\\\u00" \ "t:none" # --- Rule 7: Limit body size for RSC requests (128KB) --- SecRule TX:rsc_request "@eq 1" \ "id:900007, phase:2, deny, status:413, \ log, msg:'RSC request body too large', \ tag:'CVE-2026-23869', severity:3, chain" SecRule REQUEST_BODY_LENGTH "@gt 131072" # --- Rule 8: Block known scanners --- SecRule REQUEST_HEADERS:User-Agent \ "(?i)(?:React2ShellScanner|Nuclei.*CVE-2025-55182)" \ "id:900008, phase:1, deny, status:403, \ log, msg:'RSC vulnerability scanner blocked', severity:2"
Dashboard: Security → WAF → Custom Rules.
# ═══════════════════════════════════════════════════════════════ # Cloudflare WAF Custom Rule — CVE-2026-23869 # Dashboard → Security → WAF → Custom Rules → Create Rule # ═══════════════════════════════════════════════════════════════ # --- Rule 1: Block exploit payloads (Pro/Business — 8KB inspect) --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) and ( (http.request.body.size ge 8192) or (http.request.body.raw matches "(?i)charset=") or (http.request.body.raw contains "\\u00") or ( (http.request.body.raw contains "_formData") and (http.request.body.raw contains "_response") and (http.request.body.raw contains "resolved_model") and (http.request.body.raw contains ":constructor") ) ) Action: Block # --- Rule 2: Block self-reference pattern --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) and (http.request.body.raw matches "\$@[0-9]") Action: Block # --- Rule 3: Rate limit RSC endpoints --- (expression) (http.request.method eq "POST") and (any(http.request.headers.names[*] matches "(?i)(?:next-action|rsc-action-id)")) Action: Rate Limit — 30 requests / 60s per IP # --- Managed Rules (enabled by default) --- Rule IDs (Paid): 33aa8a8a948b48b28d40450c5fb92fba ← Primary exploit bc1aee59731c488ca8b5314615fce168 ← Bypass variants 2694f1610c0b471393b21aef102ec699 ← Promise recursion DoS 1d54691cb822465183cb49e2f562cf5c ← Scanner detection Rule IDs (Free): 2b5d06e34a814a889bee9a0699702280 ← Primary exploit cbdd3f48396e4b7389d6efd174746aff ← Bypass variants
Console → WAF → Web ACL → Add custom rule.
// ═══════════════════════════════════════════════════════════ // AWS WAF Custom Rule — CVE-2026-23869 // Deploy via Console → WAF → Web ACL → Rules → Add custom // ═══════════════════════════════════════════════════════════ { "Name": "ReactRSC_CVE_2026_23869", "Priority": 10, "Action": { "Block": {} }, "Statement": { "AndStatement": { "Statements": [ // Condition 1: POST method only { "RegexMatchStatement": { "FieldToMatch": { "Method": {} }, "RegexString": "POST", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } }, // Condition 2: RSC header present { "RegexMatchStatement": { "FieldToMatch": { "Headers": { "MatchPattern": { "All": {} }, "MatchScope": "KEY", "OversizeHandling": "MATCH" } }, "RegexString": "(?i)(?:next-action|rsc-action-id)", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } }, // Condition 3: Malicious body patterns { "OrStatement": { "Statements": [ { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "\\$\\@", "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE_UNI" }, { "Priority": 1, "Type": "JS_DECODE" }, { "Priority": 2, "Type": "UTF8_TO_UNICODE" } ] } }, { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "(?i)resolved_model[\"']|:constructor[\"']|_response[\"']\\s*:|_formData[\"']\\s*:", "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE_UNI" }, { "Priority": 1, "Type": "JS_DECODE" }, { "Priority": 2, "Type": "UTF8_TO_UNICODE" } ] } }, { "RegexMatchStatement": { "FieldToMatch": { "Body": { "OversizeHandling": "MATCH" } }, "RegexString": "(?i)charset\\s*=", "TextTransformations": [ { "Priority": 0, "Type": "NONE" } ] } } ] } } ] } }, "VisibilityConfig": { "CloudWatchMetricsEnabled": true, "MetricName": "ReactRSC_CVE_2026_23869", "SampledRequestsEnabled": true } } // Managed Rule (also enable): // AWSManagedRulesKnownBadInputsRuleSet v1.24+
Portal → Application Gateway / Front Door → WAF Policy.
// ═══════════════════════════════════════════════════════════ // Azure WAF Custom Rule — CVE-2026-23869 // Portal → Application Gateway / Front Door → WAF Policy // ═══════════════════════════════════════════════════════════ { "name": "BlockReactRSCExploit", "priority": 1, "ruleType": "MatchRule", "action": "Block", "matchConditions": [ { // Condition 1: Request has RSC action header "matchVariables": [ { "variableName": "RequestHeaders", "selector": "next-action" } ], "operator": "Any" }, { // Condition 2: Body contains exploit patterns "matchVariables": [ { "variableName": "PostArgs" } ], "operator": "Contains", "matchValues": [ "constructor", "__proto__", "prototype", "_response", "resolved_model", "_formData", "$@" ], "transforms": [ "Lowercase", "UrlDecode", "RemoveNulls" ] } ] }
Console → Network Security → Cloud Armor.
# ═══════════════════════════════════════════════════════════ # Google Cloud Armor — CVE-2026-23869 # Console → Network Security → Cloud Armor → Policy → Rules # ═══════════════════════════════════════════════════════════ # --- Rule 1: Use preconfigured WAF rules --- Condition: ( has(request.headers['next-action']) || has(request.headers['rsc-action-id']) ) && evaluatePreconfiguredWaf('cve-canary', { 'sensitivity': 0, 'opt_in_rule_ids': [ 'google-mrs-v202512-id000001-rce', 'google-mrs-v202512-id000002-rce' ] }) Action: deny-403 Priority: 100 # --- Rule 2: Rate limit RSC endpoints --- Condition: has(request.headers['next-action']) || has(request.headers['rsc-action-id']) Action: rate_based_ban Rate limit: 50 requests / 60s per IP Ban duration: 300s Priority: 200
Security Center → Application Security → Custom Rules.
// ═══════════════════════════════════════════════════════════ // Akamai WAF — CVE-2026-23869 // Security Center → Application Security → Custom Rules // ═══════════════════════════════════════════════════════════ { "name": "react2shell_cve_2026_23869", "conditions": { "all": [ { "type": "requestMethodMatch", "positiveMatch": true, "values": ["POST"] }, { "type": "requestHeaderMatch", "positiveMatch": true, "headerName": "*", "matchOperator": "MATCHES_REGEX", "matchValue": "(?i)(?:next-action|rsc-action-id)" }, { "any": [ { "type": "contentLengthMatch", "positiveMatch": true, "matchOperator": "GREATER_THAN", "matchValue": 16384 }, { "type": "requestBodyMatch", "matchOperator": "MATCHES_REGEX", "matchValue": "(?i)content-type\\s*:[^\\r\\n]*charset\\s*=" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "\\u00" }, { "all": [ { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "resolved_model" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": ":constructor" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "_response" }, { "type": "requestBodyMatch", "matchOperator": "CONTAINS", "matchValue": "_formData" } ] } ] } ] }, "action": "DENY" }
Webserver Config as an Additional Defense Layer
Even when WAF is bypassed, server-level limits still significantly reduce the blast radius. Minimum config: body limit 128KB, timeout 30 seconds.
# ═══════════════════════════════════════════════════ # Nginx — CVE-2026-23869 Protection # Add to server{} or location{} block # ═══════════════════════════════════════════════════ # --- Detect RSC headers via map --- map $http_next_action $is_rsc_next { default 0; "~." 1; } map $http_rsc_action_id $is_rsc_action { default 0; "~." 1; } map "$is_rsc_next$is_rsc_action" $is_rsc { default 0; "~1" 1; } # --- Rate limiting zone --- limit_req_zone $binary_remote_addr zone=rsc_limit:10m rate=10r/s; server { # Max body size (payload limit) client_max_body_size 128k; # Short timeout to kill hung connections proxy_read_timeout 30s; proxy_send_timeout 30s; proxy_connect_timeout 10s; location / { # Block known scanners if ($http_user_agent ~* "(React2ShellScanner|Nuclei.*CVE-2025-55182)") { return 403; } # Apply rate limit on RSC requests limit_req zone=rsc_limit burst=5 nodelay; # Reject text/x-component on non-RSC routes if ($http_accept ~* "text/x-component") { # Only allow if route is expected RSC endpoint # Customize per your app routing } proxy_pass http://backend; } }
# ═══════════════════════════════════════════════════ # Apache — CVE-2026-23869 Protection # Add to VirtualHost or .htaccess # Requires: mod_rewrite, mod_headers, mod_ratelimit # ═══════════════════════════════════════════════════ RewriteEngine On # --- Block known vulnerability scanners --- RewriteCond %{HTTP_USER_AGENT} "(?i)(React2ShellScanner|Nuclei.*CVE-2025-55182)" RewriteRule .* - [F,L] # --- Limit request body size (128KB) --- LimitRequestBody 131072 # --- Timeout settings --- TimeOut 30 ProxyTimeout 30 # --- Block RSC exploit patterns (requires mod_security or mod_rewrite) --- # Block if Next-Action header + suspicious body patterns SetEnvIfNoCase ^Next-Action$ "." RSC_REQUEST SetEnvIfNoCase ^RSC-Action-Id$ "." RSC_REQUEST # Rate limit RSC endpoints (mod_ratelimit) <If "env('RSC_REQUEST') == '1'"> SetOutputFilter RATE_LIMIT SetEnv rate-limit 512 </If> # --- If using mod_security, include the ModSecurity rules above ---
# ═══════════════════════════════════════════════════ # HAProxy — CVE-2026-23869 Protection # ═══════════════════════════════════════════════════ frontend http_front bind *:443 ssl crt /etc/ssl/cert.pem # --- Detect RSC headers --- acl is_rsc_next req.hdr(next-action) -m found acl is_rsc_action req.hdr(rsc-action-id) -m found acl is_rsc is_rsc_next or is_rsc_action acl is_post method POST # --- Rate limit: 100 req / 10s per IP --- stick-table type ip size 100k expire 60s store http_req_rate(10s) acl rsc_rate_exceeded sc0_http_req_rate gt 100 http-request track-sc0 src if is_rsc is_post http-request deny deny_status 429 if is_rsc is_post rsc_rate_exceeded # --- Block oversized RSC requests (128KB) --- acl body_too_large req.body_size gt 131072 http-request deny deny_status 413 if is_rsc is_post body_too_large # --- Block scanner user agents --- acl is_scanner req.hdr(user-agent) -i -m reg "React2ShellScanner|Nuclei.*CVE-2025" http-request deny deny_status 403 if is_scanner # --- Timeouts --- timeout client 30s timeout server 30s timeout connect 10s default_backend app_servers backend app_servers server app1 127.0.0.1:3000 check
# ═══════════════════════════════════════════════════ # Caddy — CVE-2026-23869 Protection # Caddyfile configuration # ═══════════════════════════════════════════════════ example.com { # --- Rate limit RSC endpoints --- rate_limit { zone rsc_zone { key {remote_host} events 30 window 60s } match { method POST header Next-Action * } } # --- Request body size limit --- request_body { max_size 128KB } # --- Block scanner user agents --- @scanner { header_regexp User-Agent "(?i)(React2ShellScanner|Nuclei.*CVE-2025)" } respond @scanner 403 # --- Timeouts --- servers { timeouts { read_body 30s read_header 10s write 30s idle 120s } } reverse_proxy localhost:3000 }
Patch & Update
WAF and webserver hardening are important defense layers but not a complete fix. Upgrading to a patched version is the only way to fully remediate this vulnerability.
# npm npm install react-server-dom-webpack@19.0.5 npm install react-server-dom-parcel@19.0.5 npm install react-server-dom-turbopack@19.0.5 # Or for branch 19.1.x / 19.2.x npm install react-server-dom-webpack@19.1.6 npm install react-server-dom-webpack@19.2.5 # yarn yarn add react-server-dom-webpack@19.0.5 # pnpm pnpm add react-server-dom-webpack@19.0.5
# Ensure lockfile is updated COPY package.json package-lock.json ./ RUN npm ci # Rebuild the image docker build -t myapp:patched . docker push myapp:patched
# Verify the patched version npm ls react-server-dom-webpack \ react-server-dom-parcel \ react-server-dom-turbopack # Expected: 19.0.5, 19.1.6, or 19.2.5+
| Platform | Action | Notes |
|---|---|---|
| Vercel | Redeploy | Runtime updated automatically on redeploy. |
| Netlify | Update + redeploy | Verify Next.js runtime version in build logs. |
| AWS Amplify | Update + WAF | Enable AWSManagedRulesKnownBadInputsRuleSet v1.24+ |
| CF Pages | Enable WAF rules | Workers are already protected at the runtime layer. |
| Self-hosted | Update + WAF + WS | Apply all rules covered on this page. |
Prioritized Security Checklist
VNIS — Security & Acceleration
for Web, App & API
When CVE-2026-23869 was disclosed, VNETWORK’s VNIS immediately pushed updated protection rules, blocking exploit requests targeting React Server Components and keeping customer systems running without interruption.
VNIS WAAP is built AI-first — where AI is not just a supporting tool but the core engine for threat prediction, detection, and mitigation against increasingly sophisticated attacks.

This document is intended for informational and defensive purposes. Refer to official sources for the latest updates.