CVE-2026-44578: Next.js WebSocket SSRF
A high-severity (CVSS 8.6) server-side request forgery flaw in the Next.js WebSocket upgrade handler that enables unauthenticated attackers to coerce self-hosted Next.js servers into proxying HTTP GET requests to arbitrary internal or external destinations on port 80 — including AWS, GCP, Azure, OCI, and DigitalOcean cloud-metadata endpoints.
Executive Summary
On May 6, 2026, Vercel maintainer Tim Neutkens privately published GitHub Security Advisory GHSA-c4j6-fc7j-m34r, and on May 11, 2026 the advisory was made public alongside patched releases Next.js 15.5.16 and Next.js 16.2.5. The MITRE/GitHub CNA assigned CVE-2026-44578 on May 13, 2026 (NVD entry date), describing it as “Next.js: Server-side request forgery in applications using WebSocket upgrades” (CWE-918).
The flaw originates in packages/next/src/server/lib/router-server.ts. Next.js’s HTTP/1.1 WebSocket upgrade handler only checked whether parsedUrl.protocol was truthy before forwarding the request via the internal proxyRequest() function — it did not verify the finished and statusCode flags that the equivalent HTTP request handler had always honored. By sending a single HTTP/1.1 request with an absolute-form request-line URI (legal per RFC 7230 §5.3.2) plus the standard Connection: Upgrade / Upgrade: websocket headers, an attacker can cause the Next.js process to open an outbound TCP connection to any host reachable from the server on port 80 and pipe the response back over the same socket.
The vulnerability is rated High severity, CVSS 8.6 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N) by the GitHub CNA and NVD. It affects self-hosted Next.js applications running the built-in Node.js server on versions 13.4.13 → 15.5.15 and 16.0.0 → 16.2.4. Applications deployed on Vercel are explicitly not affected, because Vercel’s edge layer rejects the absolute-form request URIs that trigger the bug. Other managed/serverless platforms (AWS Lambda, Google Cloud Functions, Azure Functions, AWS Amplify, Netlify) are likewise not vulnerable because their request pathways do not expose the raw Node.js HTTP upgrade event.
According to research published by Hadrian (May 13, 2026), approximately 740,000 Next.js servers are indexed on Shodan globally, with roughly 118,700 directly exposed on the default next start port; a sampling of that cohort suggests on the order of ~79,000 hosts are exploitable as of disclosure. Public proof-of-concept code exists (e.g., dwisiswant0/next-16.2.4-pocs on GitHub). The CISA ADP SSVC scoring lists exploitation as “none” observed in the wild at the time of publication but flags it as automatable with partial technical impact.
CVE-2026-44578 is one of 13 advisories shipped in the coordinated Next.js May 2026 Security Release, alongside several middleware/proxy bypass, DoS, XSS, and cache-poisoning issues (CVE-2026-23870, CVE-2026-44573–44582, etc.). Follow-up releases 15.5.18 and 16.2.6 were published on May 7, 2026 with additional fixes; users should now target those (or later) versions.
1. CVE Identification, Scores, and Timeline
| Field | Value |
|---|---|
| CVE ID | CVE-2026-44578 |
| GitHub Security Advisory | GHSA-c4j6-fc7j-m34r |
| Title (per CNA) | Next.js: Server-side request forgery in applications using WebSocket upgrades |
| CWE | CWE-918 — Server-Side Request Forgery (SSRF) |
| CVSS v3.1 Base Score | 8.6 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N |
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | None |
| Scope | Changed (a major driver of the high score) |
| Confidentiality / Integrity / Availability impact | High / None / None |
| CISA ADP / SSVC | Exploitation: none observed; Automatable: yes; Technical Impact: partial |
| Snyk score | 7.7 (High) per Snyk’s independent assessment (SNYK-JS-NEXT-16638682) |
| Assigner / CNA | GitHub_M (GitHub Security Advisories) |
| CVE Reserved | 2026-05-06 |
| Advisory Published (GHSA) | 2026-05-06 (privately) → public 2026-05-11 |
| NVD/CVE Record Published | 2026-05-13 |
| NVD Record Last Modified | 2026-05-13 |
| Reporter / Credited | Disclosed by Vercel’s Tim Neutkens (@timneutkens); the original external reporter is not named in the public advisory (discovery: UNKNOWN in the CVE record JSON) |
| Affected ecosystem / package | npm next |
| Affected versions | >= 13.4.13, < 15.5.16 and >= 16.0.0, < 16.2.5 (self-hosted only) |
| Patched versions | 15.5.16 and 16.2.5 (subsequently superseded by 15.5.18 and 16.2.6 on May 7, 2026) |
| Vercel-hosted | Not affected |
| Public exploit / PoC | Yes — github.com/dwisiswant0/next-16.2.4-pocs and others |
Discovery and Disclosure Timeline
| Date (2026) | Event |
|---|---|
| May 4 | Fix committed to Next.js canary branch (commit prefix 846b7f82, per Hadrian’s analysis) |
| May 6 | CVE-2026-44578 reserved; GHSA-c4j6-fc7j-m34r published by timneutkens on the vercel/next.js repository (initially private/limited visibility) |
| May 7 | Coordinated public release of Next.js 15.5.16 and 16.2.5 with the patch; “Next.js May 2026 Security Release” changelog posted by Vercel addressing 13 advisories. Same day, follow-up releases 15.5.18 and 16.2.6 ship additional fixes (announced via the official @nextjs X account on May 7). |
| May 11 | GitHub Advisory Database publishes GHSA-c4j6-fc7j-m34r; reviewed and indexed |
| May 13 | NVD record for CVE-2026-44578 published; CISA ADP enrichment added |
| Within days of May 11 | Public proof-of-concept code published (e.g., dwisiswant0/next-16.2.4-pocs); media coverage by Cybersecurity News, Cyberpress, GBHackers, Hadrian, OpenCVE, Tenable, Snyk, TheHackerWire |
Related and Adjacent Vulnerabilities
CVE-2026-44578 is the most severe individual finding in a much larger May 2026 batch of Next.js security advisories. The broader release context is important because organizations should treat upgrading as a single, comprehensive operation rather than addressing this CVE in isolation.
Other advisories in the May 2026 Next.js Security Release (all fixed in 15.5.16/15.5.18 and 16.2.5/16.2.6):
| CVE | GHSA | Severity | Component / Class |
|---|---|---|---|
| CVE-2026-23870 (upstream React) | GHSA-8h8q-6873-q5fj | High | DoS via React Server Components / “Flight” protocol deserialization |
| CVE-2026-44575 | GHSA-267c-6grr-h53f | High | App Router middleware bypass via segment-prefetch URLs |
| — | GHSA-26hh-7cqf-hhc6 | High | Incomplete-fix follow-up for segment-prefetch bypass |
| CVE-2026-44574 | GHSA-492v-c6pp-mqqv | High | Middleware bypass via dynamic-route parameter injection |
| CVE-2026-44578 | GHSA-c4j6-fc7j-m34r | High | WebSocket-upgrade SSRF (this advisory) |
| CVE-2026-44573 | GHSA-36qx-fr4f-26g5 | High | Pages Router i18n default-locale data-route middleware bypass |
| CVE-2026-44579 | GHSA-mg66-mrh9-m8jx | High | DoS via connection exhaustion in Cache Components |
| CVE-2026-44577 | GHSA-h64f-5h5j-jqjh | Moderate | Image Optimization API DoS (self-hosted only) |
| CVE-2026-44581 | GHSA-ffhc-5mcf-pf4q | Moderate | XSS via CSP-nonce parsing in App Router |
| CVE-2026-44580 | GHSA-gx5p-jg67-6x7h | Moderate | XSS in beforeInteractive scripts with untrusted input |
| CVE-2026-44576 | GHSA-wfc6-r584-vfw7 | Moderate | RSC vs. HTML cache poisoning / confusion |
| CVE-2026-44582 | GHSA-vfv6-92ff-j949 | Low | Cache poisoning via collisions in RSC cache-busting (_rsc weak hash) |
| — | GHSA-3g8h-86w9-wvmq | Moderate | Cache poisoning of middleware redirects via x-nextjs-data |
Historically related Next.js CVEs (recommended context):
- CVE-2025-29927 (March 2025, CVSS 9.1 Critical) — the now-infamous middleware authorization bypass exploitable by adding the
x-middleware-subrequestheader. Discovered by Rachid Allam (“zhero”). Fixed in 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3. CVE-2026-44578 shares the same broad theme — boundary-handling bugs in Next.js’s request-routing layer — but is a different code path (HTTPupgradeevent, not the middleware subrequest pseudo-protocol). - CVE-2025-66478 (December 2025, CVSS 10.0 Critical) — RCE in the React Server Components protocol.
- CVE-2025-55183 / CVE-2025-55184 (December 2025) — RSC source code exposure and DoS.
- CVE-2026-23869 / CVE-2026-27979 / CVE-2026-29057 — separate earlier-2026 issues (
http-proxyrequest smuggling in rewrites; RSC DoS); patched by backported 15.x/16.x releases.
The repeated appearance of HTTP-routing and proxy-layer flaws in Next.js’s request pipeline is itself a notable security-engineering observation: a Hadrian/dwisiswant0 analysis of the May 2026 batch describes “a pattern of canonicalisation gaps between the edge router and the internal data fetcher [that] runs through five of the six High-severity issues.”
2. Technical Deep Dive
2.1 Where the Bug Lives
The vulnerability resides in the upgradeHandler function of packages/next/src/server/lib/router-server.ts, the file that implements Next.js’s built-in HTTP/1.1 router used when the application is started via next start (or any equivalent self-host invocation that uses the bundled Node.js server).
Next.js’s router-server contains two handlers that share the same resolveRoutes() helper for figuring out whether an inbound request is destined for an internal route, a static asset, an external rewrite, or something else:
- The HTTP request handler (for normal GET/POST/etc.).
- The HTTP upgrade handler (for WebSocket upgrades — triggered by Node.js’s
'upgrade'event whenever it sees aConnection: UpgradeplusUpgrade: <protocol>pair).
resolveRoutes() returns several fields, including parsedUrl, matchedOutput, finished (a boolean indicating routing finished and produced a definitive instruction), and statusCode (set when routing wants to short-circuit the request, e.g. to send a 308 redirect).
The HTTP request handler had always gated its call to proxyRequest() on the conjunction of finished (routing actually completed and approved an external rewrite) and parsedUrl.protocol (the resolved destination is an external URL). The upgrade handler only checked parsedUrl.protocol. That asymmetry is the entire bug.
2.2 The Vulnerable Code (pre-patch)
// Vulnerable: only matchedOutput and parsedUrl are destructured
const { matchedOutput, parsedUrl } = await resolveRoutes({
req,
res,
isUpgradeReq: true,
signal: signalFromNodeResponse(socket),
})
if (matchedOutput) {
return socket.end()
}
if (parsedUrl.protocol) {
return await proxyRequest(req, socket, parsedUrl, head)
}
2.3 The Patched Code (15.5.16 / 16.2.5)
// Patched: finished and statusCode are now destructured and checked
const { finished, matchedOutput, parsedUrl, statusCode } =
await resolveRoutes({
req,
res,
isUpgradeReq: true,
signal: signalFromNodeResponse(socket),
})
if (matchedOutput) {
return socket.end()
}
if (finished && parsedUrl.protocol) {
if (!statusCode) {
return await proxyRequest(req, socket, parsedUrl, head)
}
return socket.end()
}
The official advisory describes the fix in one sentence: “We now apply the same safety checks to WebSocket upgrade handling that already existed for normal HTTP requests, so upgrade requests are only proxied when routing has explicitly marked them as safe external rewrites.” (Vercel/GHSA-c4j6-fc7j-m34r).
2.4 How parsedUrl.protocol Gets Set Without Routing Approval
resolveRoutes() begins with:
let parsedUrl = url.parse(req.url || '', true) as NextUrlWithParsedQuery
Node.js’s http module preserves the request-target verbatim in req.url. HTTP/1.1 (RFC 7230 §5.3.2) permits an absolute-form request-target, meaning a client may legally send:
GET http://internal-service:8080/secret HTTP/1.1
Host: public-app.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
When req.url is http://internal-service:8080/secret, url.parse() returns a parsedUrl whose protocol field is 'http:'. That alone is enough to satisfy the vulnerable if (parsedUrl.protocol) check, and proxyRequest() is invoked. Critically, middleware never runs on this path — this is not an x-middleware-subrequest-style bypass. It is a routing-completion bug.
2.5 The normalizeRepeatedSlashes Interaction (Why It Was Missed)
resolveRoutes() has an early-exit branch that normalizes URLs containing // or \:
if (urlNoQuery?.match(/(\\|\/\/)/)) {
parsedUrl = url.parse(normalizeRepeatedSlashes(req.url!), true)
return {
parsedUrl,
resHeaders,
finished: true,
statusCode: 308,
}
}
The // inside http:// causes routing to take this early-exit branch and return finished: true, statusCode: 308 — i.e., a redirect was produced, not an approved external proxy target. Because the vulnerable upgrade handler ignored both flags, it proceeded to call proxyRequest() despite the routing layer signaling “do not proxy.”
This is also why the destination port is forced to 80: the URL-normalization step strips the explicit port (:8080, :9876, etc.) from the URI, and the downstream http-proxy library / http.request() call falls back to Node’s default of port 80. The attacker can specify any host but only port 80 is reachable as the SSRF target.
2.6 Exploitation Walk-through
A working PoC requires nothing more than curl or a raw TCP client. The single HTTP request:
GET http://169.254.169.254/latest/meta-data/ HTTP/1.1
Host: target.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Step-by-step:
- Attacker opens a TCP connection to the public Next.js server on whatever port
next startis listening on (default 3000, but irrelevant). - The request line uses an absolute-form URI pointing to the internal target (here AWS IMDSv1).
- Node.js sees the
Connection: Upgrade+Upgrade: websocketpair and fires its'upgrade'event instead of the normal'request'event. - Next.js’s upgrade handler calls
resolveRoutes(), gets backparsedUrl.protocol = 'http:'(because the request line was an absolute URI), and — without checkingfinishedorstatusCode— callsproxyRequest(). proxyRequest()opens a TCP connection to169.254.169.254:80and forwards a GET to/latest/meta-data/.- The internal service’s response is piped back to the attacker over the original hijacked socket.
2.7 What the Attacker Can and Cannot Do
| Capability | Status |
|---|---|
| Unauthenticated exploitation | Yes |
| Single-request exploitation | Yes |
| HTTP method | GET only — the upgrade pathway forwards a GET; arbitrary methods are not possible |
| Destination port | Port 80 only (URL-normalization strips explicit ports) |
| Destination host | Any host reachable from the Next.js process (RFC 1918, link-local 169.254/16, public Internet) |
| Read response body | Yes — response is piped back over the hijacked socket |
| Attack cloud metadata services | AWS IMDSv1 (169.254.169.254), Azure IMDS, Oracle OCI (/opc/v1/...), DigitalOcean metadata. GCP metadata (metadata.google.internal) rejects requests carrying Upgrade: websocket with HTTP 400, so GCP IMDS is not exploitable. AWS IMDSv2 is largely not exploitable because it requires a PUT /latest/api/token to obtain a session token (this SSRF can only emit GETs) and IMDSv2 also rejects requests carrying X-Forwarded-For, which http-proxy adds by default. |
| Bypass middleware/auth | Yes — middleware is never invoked on the upgrade path |
| Persistent / RCE | No direct RCE; impact is confidentiality (data exfiltration, credential theft) |
2.8 Affected Versions, Configurations, and Deployment Patterns
Vulnerable versions (npm next):
>= 13.4.13and< 15.5.16>= 16.0.0and< 16.2.5
That spans roughly three years of Next.js releases (13.4.13 was the version in which the relevant upgrade-handler code path landed) including the entire 14.x line.
Required conditions for exploitability:
- Self-hosted deployment using the built-in Next.js Node.js server (
next start, oroutput: standalone, oroutput: server). - The Next.js process must be reachable from the attacker (directly, or behind a reverse proxy that forwards absolute-form URIs and
Upgradeheaders verbatim — most mainstream proxies reject absolute-form URIs by default, providing an accidental mitigation). - An internal HTTP service reachable from the Next.js host on port 80.
Configuration is irrelevant: WebSocket upgrade handling is built into the Next.js router and cannot be disabled via next.config.js. The bug does not depend on App Router vs. Pages Router, on whether middleware exists, on i18n, on ISR, or on any other application-level setting.
Deployments that ARE vulnerable:
- Bare-metal / VM deployments running
next startdirectly. - Docker containers exposing the Next.js port directly to untrusted networks.
- Kubernetes deployments where the Next.js pod is reachable from ingress without a reverse proxy that rejects absolute-form URIs.
- Self-hosted staging/dev environments running affected versions.
Deployments that are NOT vulnerable:
- Vercel (explicitly noted by the advisory — Vercel’s platform infrastructure rejects the exploit-shaped requests before they reach the Next.js process).
- AWS Lambda, Google Cloud Functions, Azure Functions — serverless platforms do not expose the raw Node.js
'upgrade'event. - AWS Amplify, Netlify, Cloudflare Pages, and other managed platforms that reject absolute-form request URIs before the Next.js process receives them.
- React Server Components packages themselves are not vulnerable to this CVE (RSC has its own separate advisories in the same release batch, notably CVE-2026-23870, which is a different class of bug — a DoS via the “Flight” protocol).
3. Detection — How to Check if You Are Affected
3.1 Real-World Impact
Public proof-of-concept code was released within days of the advisory (see github.com/dwisiswant0/next-16.2.4-pocs, which contains PoCs for the entire May 2026 batch, including poc/CVE-2026-44578_GHSA-c4j6-fc7j-m34r/). Confirmed exploit primitives include:
- Cloud-metadata credential theft — On AWS instances still permitting IMDSv1, a single GET to
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>returns STS credentials whose IAM permissions are whatever is attached to the EC2 instance role. These often grant access to S3, RDS, Secrets Manager, and other services. - Azure managed-identity tokens retrievable from the Azure IMDS endpoint.
- Internal-network reconnaissance of HTTP services on RFC 1918 / link-local addresses on port 80 — internal admin panels, dashboards, dev tools, Prometheus/Grafana instances, etcd/Consul HTTP APIs, etc.
- Exfiltration of plaintext secrets served by misconfigured internal HTTP endpoints (Git config services, build-info pages, debug routes, configuration dumps).
- API-key harvesting from internal microservices that trust the application VPC perimeter.
Researchers report that “attackers can extract cloud credentials in seconds using only HTTP GET requests to publicly reachable Next.js servers” (Hadrian, 2026-05-13). Because the request originates from the application server itself, it bypasses external WAFs, IP allow-lists, and security groups that gate inbound traffic but not the server’s outbound traffic.
3.2 Identifying the Next.js Version in Use
The single most important detection step is to confirm which version of the next package your project resolves to. Multiple authoritative methods exist:
From the project root (each command prints the installed next version):
# npm (works for both npm and projects that include a package-lock.json)
npm list next
# or
npm ls next --depth=0
# pnpm
pnpm list next
# or
pnpm why next
# yarn classic
yarn list --pattern next
# yarn berry (v2+)
yarn why next
# Direct inspection of the installed copy
cat node_modules/next/package.json | grep '"version"'
# bun
bun pm ls | grep next
Static inspection of lockfiles (works even without node_modules installed):
# npm
jq '.packages["node_modules/next"].version' package-lock.json # lockfileVersion 2+
# pnpm
grep -A1 '^ next:' pnpm-lock.yaml | head
# yarn classic
grep -A2 '^next@' yarn.lock
# yarn berry
grep -A2 '"next@' yarn.lock
Compare the resolved version against the affected ranges:
- Vulnerable:
>= 13.4.13 < 15.5.16OR>= 16.0.0 < 16.2.5. - Recommended patched targets:
>= 15.5.18on the 15.x track and>= 16.2.6on the 16.x track (the May 7, 2026 follow-up releases that include additional fixes from the same batch).
3.3 Determining if You Are “Self-Hosted”
The CVE only applies to applications using Next.js’s built-in Node.js server. You can verify your deployment falls in scope by checking:
| Indicator | Self-hosted (in scope) | Managed (out of scope) |
|---|---|---|
| Start command | next start, node server.js (custom server), output: 'standalone' + node .next/standalone/server.js | Vercel CLI / Vercel platform, AWS Lambda adapter, @netlify/plugin-nextjs, OpenNext, AWS Amplify build |
| Hosting | Bare-metal, VM, EC2, GCE, Azure VM, K8s, Docker, on-prem | Vercel, Netlify, AWS Amplify, Cloudflare Pages, Lambda |
| Listens on a TCP port directly | Yes — typically 3000 / 80 / 443 | No — invoked per request by the platform |
If any reverse proxy (nginx, Caddy, HAProxy, Cloudflare, AWS ALB with strict HTTP parsing) sits in front of the Next.js process and rejects absolute-form HTTP request lines or strips upgrade headers, you have an accidental mitigation — but this should be validated, not assumed.
3.4 Fingerprinting Deployed Apps
For external assessment (e.g., security teams scanning their own externally-reachable assets):
- HTML/asset fingerprinting: Next.js typically emits
<script>references like/_next/static/chunks/...and<link rel="preload">for chunked builds. The presence of/_next/static/is a strong indicator. - HTTP headers: Next.js sets
x-powered-by: Next.jsby default unless explicitly disabled viapoweredByHeader: false. - Build manifest:
/_next/static/<buildId>/_buildManifest.jsis reachable on most exposed deployments and confirms a Next.js app. - Server header on
next start: Node.js’s defaultconnection: keep-aliveand absence of a server reverse proxy fingerprint can suggest direct Node.js exposure (i.e., higher risk).
Note on safe in-scope testing: A non-destructive check is to send a benign absolute-form upgrade request pointed at a host you control (e.g., http://your-canary-domain.example/) and observe whether your canary receives the inbound GET. Pointing the probe at IMDS endpoints from a third-party network is not a benign test — it produces an actual SSRF from the target and may be considered hostile.
3.5 Detection via Standard Tooling
| Tool | Detection support for CVE-2026-44578 |
|---|---|
| GitHub Advisory Database / Dependabot | Yes — GHSA-c4j6-fc7j-m34r is indexed and Dependabot raises an alert for affected next versions. |
npm audit | Yes — reports the advisory via npm’s GitHub-sourced advisory feed once the local audit DB syncs. |
| Snyk | Yes — SNYK-JS-NEXT-16638682 (severity 7.7 per Snyk’s own re-scoring). Snyk CLI: snyk test. |
| OSV.dev / OSV-Scanner | Yes — the GHSA is mirrored to OSV. osv-scanner -L package-lock.json. |
| GitLab Advisory Database | Yes — published under the same CVE ID. |
| Socket.dev | Yes — flags next versions in the vulnerable range during install/PR review. |
| Tenable / Qualys / Rapid7 | Yes — indexed via NVD feed. |
| OpenCVE / Feedly CVE / TheHackerWire | Yes — aggregated CVE feeds carry the record. |
Example quick-scan commands:
npm audit --json | jq '.vulnerabilities.next'
npx --yes osv-scanner -L package-lock.json
snyk test
3.6 Log and Network Indicators of Exploitation
Detecting actual exploitation is harder than detecting vulnerable code because HTTP upgrade requests in Node.js are socket-level events that do not complete a normal request/response cycle. Many Next.js deployments do not log them at all.
On the Next.js process / application logs:
- The string
Failed to proxy http:/(note the single slash — this is the artifact ofnormalizeRepeatedSlashescollapsinghttp://tohttp:/) appearing in stderr is, per Hadrian, “the normalisation fingerprint and is a reliable indicator that the vulnerable SSRF path fired.”
On reverse-proxy / access logs (if any):
- Request lines that begin with
http://orhttps://(absolute-form URIs) — almost always anomalous and a strong exploit signature. - The combination of an absolute-form URI plus
Upgrade: websocketis the exact exploit shape and does not occur in normal traffic.
Network egress monitoring:
- New outbound connections from the Next.js host to
169.254.169.254:80,metadata.google.internal:80,100.100.100.200:80(Alibaba), or any RFC 1918 / link-local address on port 80 that does not correspond to a configured rewrite destination. - Bursty WebSocket-upgrade traffic from external sources targeting endpoints that do not normally accept WebSockets.
Cloud-side detection (AWS):
- VPC Flow Logs showing the EC2 instance contacting 169.254.169.254 unexpectedly.
- CloudTrail anomalies for the EC2 instance role: STS
AssumeRoleor unusualGetObjectpatterns shortly after WebSocket-upgrade traffic spikes inbound.
Public-exposure assessment (third-party services):
- Shodan queries can identify directly-exposed Next.js servers; Hadrian’s estimate of ~79,000 exploitable hosts came from sampling Shodan’s index.
- Internal asset-inventory tools (EASM platforms) should already be flagging vulnerable
nextversions via standard SCA enrichment.
4. Remediation — Patches, Workarounds, and Verification
4.1 The Patched Versions
Upgrade to a patched release. The advisory’s minimum-viable patched versions are:
- 15.5.16 for the 15.x track
- 16.2.5 for the 16.x track
However, on May 7, 2026 (one day after the security release), Vercel shipped follow-up releases 15.5.18 and 16.2.6 that include additional fixes from the same 13-advisory coordinated batch. The official @nextjs X account explicitly recommended these as the upgrade target. In practice, you should upgrade to >= 15.5.18 or >= 16.2.6 (or any later release in those minors) so that you simultaneously close the other related advisories.
| Current track | Minimum fix | Recommended target |
|---|---|---|
| Next.js 13.x (13.4.13+) | No patch available — must upgrade to 14.x → 15.x → 16.x | 15.5.18 LTS or 16.2.6 |
| Next.js 14.x (entire line) | No patch available — Next.js 14 reached End-of-Life on October 26, 2025 | 15.5.18 LTS or 16.2.6 |
| Next.js 15.x (< 15.5.16) | 15.5.16 | 15.5.18 LTS |
| Next.js 16.x (< 16.2.5) | 16.2.5 | 16.2.6 |
End-of-life caveat: Per Vercel’s published support schedule, Next.js 14 reached EOL on Oct 26, 2025, and Next.js 13 was already EOL at the time of this advisory. There are no patched 13.x or 14.x releases — operators on those branches must perform a major-version migration.
4.2 Step-by-Step Upgrade Commands
npm:
# Target the 15.x LTS track
npm install --save-exact next@15.5.18
# Or target the 16.x track
npm install --save-exact next@16.2.6
# React peer dependencies for the same security batch
npm install react@19.1.7 react-dom@19.1.7
# (the relevant react-server-dom-* package version must match your bundler:
# 19.0.6, 19.1.7, or 19.2.6 — see Vercel's May 2026 advisory)
# Refresh the lockfile and verify
npm ci
npm list next
pnpm:
pnpm add next@16.2.6
pnpm add react@19.1.7 react-dom@19.1.7
pnpm install --frozen-lockfile
pnpm list next
yarn classic (v1):
yarn add next@16.2.6 --exact
yarn add react@19.1.7 react-dom@19.1.7 --exact
yarn install --frozen-lockfile
yarn list --pattern next
yarn berry (v2+):
yarn up next@16.2.6
yarn up react@19.1.7 react-dom@19.1.7
yarn install --immutable
yarn why next
bun:
bun add next@16.2.6
bun add react@19.1.7 react-dom@19.1.7
bun install --frozen-lockfile
4.3 Migration Considerations
If you are already on a recent 15.x release, the bump to 15.5.18 is a patch-level update and should be drop-in. The 16.x track introduces a handful of breaking changes documented in Vercel’s Upgrading: Version 16 guide — most relevant items:
- Async Request APIs are mandatory in 16.x. Synchronous access to
params,searchParams,cookies(),headers(), anddraftMode()was deprecated in 15 and removed in 16. Usenpx @next/codemod@latest next-async-request-api .to migrate. middleware.jsis being renamed toproxy.jsin 16.x. The legacymiddlewarefilename and themiddlewarenamed export are deprecated; the newproxyruntime is Node.js-only (the edge runtime continues to be supported under the legacymiddlewarename).- Image-metadata generators now receive
paramsandidas Promises. - Turbopack config moved from
experimental.turbopackto the top-levelturbopackkey. - PPR (Partial Pre-Rendering) in 16 is reorganized under Cache Components — if you rely on the canary PPR you may want to stay on the 15.x canary track for now.
For most security-driven upgrades, the 15.5.18 LTS path is the lower-risk choice if your application is currently on 15.x — Next.js 15 LTS support runs through October 21, 2026, giving you a runway to plan the 16.x migration separately.
Ecosystem dependency check: Some popular Next.js plugins lag the security release. For example, nextra@4.6.1 is known to fail to build under Next.js 16.2.x (Nextra GitHub issue #5003 reports both Webpack and Turbopack build failures). Verify your critical plugins (nextra, next-auth, next-intl, next-mdx-remote, etc.) have Next 16.2.x-compatible releases, or pin to the 15.5.18 LTS track until they catch up.
4.4 Workarounds for Operators Who Cannot Upgrade Immediately
The official advisory’s workaround text is concise: “If you cannot upgrade immediately, do not expose the origin server directly to untrusted networks. If WebSocket upgrades are not required, block them at your reverse proxy or load balancer, and restrict origin egress to internal networks and metadata services where possible.”
In practice, defense-in-depth comprises four layers:
1. Reject absolute-form request URIs at the reverse proxy.
The exact exploit signature is an HTTP request line that begins with http:// or https:// (instead of /). nginx, Caddy, and HAProxy already reject such request lines by default for normal HTTP traffic, but you can add an explicit guard. Example nginx rule:
# /etc/nginx/conf.d/cve-2026-44578.conf
if ($request_uri ~* "^https?://") {
return 400;
}
Example HAProxy:
acl absolute_uri url_reg ^https?://
http-request deny if absolute_uri
Example Caddy:
@absuri path_regexp ^https?://
respond @absuri 400
2. Block or scrutinize WebSocket-upgrade requests.
If your application does not use WebSockets at all, block the Upgrade header pathway entirely:
# Block all WebSocket upgrades — only safe if your app doesn't use WS
map $http_upgrade $block_upgrade {
default 1;
"" 0;
}
server {
if ($block_upgrade) { return 400; }
}
If your app does use WebSockets, do not apply a blanket block — instead pair the absolute-form rule above with an alert on the combination of an absolute-form URI plus Upgrade: websocket. That combination is the precise exploit shape and does not occur in legitimate traffic.
3. Enforce IMDSv2 on AWS.
The most damaging cloud-credential theft path is AWS IMDSv1. Requiring IMDSv2 on every EC2 instance (HttpTokens=required) neutralizes the credential-theft impact even on unpatched Next.js because IMDSv2 requires a PUT to mint a session token (this SSRF can only emit GETs) and IMDSv2 rejects requests carrying X-Forwarded-For (which http-proxy adds by default). AWS CLI:
aws ec2 modify-instance-metadata-options \
--instance-id i-0123456789abcdef0 \
--http-tokens required \
--http-put-response-hop-limit 1
Apply at scale via SSM Automation document AWSSupport-EnforceIMDSv2 or Terraform/CDK.
4. Restrict egress from the application server.
If the application server has no legitimate need to talk to internal-network ranges or the metadata IP, deny that egress at the security-group / NACL / iptables / nftables / Cilium NetworkPolicy level. Example iptables snippet for an EC2 instance:
# Block outbound to the AWS IMDS unless the process explicitly needs it
iptables -A OUTPUT -d 169.254.169.254 -j REJECT
# Or restrict to a specific UID that owns the legitimate metadata-using process
iptables -A OUTPUT -d 169.254.169.254 -m owner ! --uid-owner metadata -j REJECT
5. WAF rules.
If your WAF inspects the HTTP request line (rather than just the path):
- Reject inbound requests whose request line begins with
http://orhttps://. - Alert on outbound connections from the Next.js process to cloud-metadata or RFC-1918 / link-local addresses on port 80 that do not match a configured rewrite destination.
Note: Vercel’s WAF team explicitly stated in the May 2026 release that “Vercel has not deployed new WAF rules for this release; these advisories cannot be reliably blocked at the WAF layer.” Patching is the only complete mitigation; WAF/proxy rules are stopgaps.
4.5 Verification After Patching
# 1. Confirm version
node -p "require('next/package.json').version"
# Expect 15.5.18 or 16.2.6 (or later)
# 2. Confirm the patched code path is present by inspecting the file
grep -n "finished && parsedUrl.protocol" node_modules/next/dist/server/lib/router-server.js
# Should return a match in the upgrade handler
# 3. Negative test: send a crafted upgrade request to your own staging server
# pointed at a canary domain you control, and confirm the canary did NOT receive
# the inbound GET.
printf 'GET http://canary.example/\r\nHost: staging.local\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n' | \
nc staging.local 3000
# Patched: connection closed by the server; canary receives nothing.
# Unpatched: canary receives an inbound GET / from the staging server's IP.
Restart all application processes after upgrading — next start does not hot-reload the patched code into already-running workers.
4.6 Long-Term Hardening
- Do not rely on Next.js middleware (or its 16.x successor
proxy.js) as the sole enforcement point for authentication and authorization. The May 2026 batch alone contains five separate middleware/proxy bypass advisories. Vercel’s own remediation guidance for the broader batch is to enforce authorization inside the route handler / page logic, not only at the middleware boundary. - Pin to LTS lines. Next.js 15 LTS is supported through Oct 21, 2026; subscribe to the
@nextjsX account, thenextjs.org/blogsecurity feed, and GitHub repository security alerts forvercel/next.js. - Enable Dependabot security updates on every repository that depends on
next. - Adopt least-privilege IAM roles for EC2 instances and managed identities such that even a successful credential theft has limited blast radius.
- Set
HttpTokens=requiredeverywhere (IMDSv2 mandatory). - Deploy egress-aware service-mesh policies (Cilium, Istio, Calico) to allow only explicit outbound destinations from your Next.js workloads.
- Adopt continuous SCA + EASM: combine Snyk/Dependabot/Socket for dependency-level detection with an external attack-surface tool (Hadrian, Detectify, Censys ASM) to catch directly-exposed
next startinstances in shadow IT.
5. References
Primary / Authoritative Sources
- GitHub Security Advisory (canonical) — GHSA-c4j6-fc7j-m34r
- GitHub Advisory Database mirror
- NVD entry — CVE-2026-44578
- MITRE CVE record
- Vercel May 2026 Security Release changelog
- Next.js 15.5.16 release
- Next.js 16.2.5 release
- Next.js 15.5.18 / 16.2.6 follow-up announcement (Next.js X account)
- Next.js security advisory index
Vulnerability Database Mirrors
In-Depth Technical Write-Ups
- Hadrian — Next.js WebSocket SSRF (CVE-2026-44578) — May 13, 2026; the most technically detailed public write-up, including code diffs, the
normalizeRepeatedSlashesanalysis, and the Shodan-based exposure estimate of ~79,000 hosts. - Cybersecurity News — Multiple Critical Vulnerabilities Patched in Next.js and React Server Components — May 8, 2026; overview of all 13 advisories in the batch.
- Cybersecurity News — Critical Next.js Vulnerability Exposes Cloud Credentials, API keys, and Admin Panels — May 15, 2026.
- Cyberpress — Multiple Critical Vulnerabilities Patched in Next.js and React Server Components
- Cyberpress — Critical Next.js Flaw Exposes Cloud Credentials, API Keys, and Admin Panels
- GBHackers — Multiple Critical Flaws Fixed in Next.js and React Server Components
- TheHackerWire — Next.js SSRF via Crafted WebSocket Upgrade Requests
- Stork.AI — Next.js Security Alert: 13 New CVEs & Server Component Risks
Proof-of-Concept Code
dwisiswant0/next-16.2.4-pocs— comprehensive PoC collection for the entire May 2026 batch, including a working CVE-2026-44578 exploit (seepoc/CVE-2026-44578_GHSA-c4j6-fc7j-m34r/). Authored bydwi.siswanto@projectdiscovery.io. The README explicitly states this is post-patch research, not a 0-day disclosure.
Background — Related Next.js Security History
- CVE-2025-29927 (middleware bypass via
x-middleware-subrequest) — ProjectDiscovery technical analysis - CVE-2025-29927 — Datadog Security Labs
- CVE-2025-29927 — Zscaler ThreatLabz
- CVE-2025-29927 — JFrog
- CVE-2025-29927 — OffSec
- CVE-2025-29927 PoC (
zhero/ Rachid Allam original research) - CVE-2026-23869 / 23870 (React Server Components DoS) — Vercel summary
Next.js Documentation and Upgrade Guides
Closing thoughts
Severity in context. CVE-2026-44578 is genuinely high-impact but narrower than the headline number suggests. The CVSS 8.6 score is driven mostly by the Scope: Changed (the SSRF crosses a trust boundary into the cloud-instance metadata service) and Confidentiality: High (credentials exfiltration). Crucially, it is GET-only, port-80-only, and self-hosted only. Vercel-hosted apps are not affected, serverless deployments are not affected, and AWS environments that enforce IMDSv2 dramatically reduce real-world impact even on unpatched servers. That said, the unauthenticated single-request exploitation profile, the trivial PoC (a single curl-equivalent), and the ~3-year span of affected versions (13.4.13 through 16.2.4) make immediate patching non-negotiable for any in-scope deployment.
The bigger picture. CVE-2026-44578 sits inside a much larger pattern: this is at least the third coordinated security release in eight months to ship multiple high-severity Next.js bugs related to request routing and request boundaries (CVE-2025-29927 in March 2025; CVE-2025-66478 / 55183 / 55184 in December 2025; and now the May 2026 batch of 13 CVEs). All of these issues share a common root: Next.js’s tightly integrated, multi-layer request-handling pipeline (edge router → middleware → App Router → RSC → Pages Router → built-in proxy) has repeatedly demonstrated canonicalization and trust-boundary inconsistencies between layers. Operationally, the right takeaway is the one Vercel and every independent researcher emphasized: do not rely on Next.js middleware or routing as the sole enforcement point for authentication and authorization — enforce inside the route handler, treat the framework as untrusted, and assume the next batch of routing CVEs is coming.
What to do this week. Upgrade self-hosted Next.js to 15.5.18 (LTS, recommended for most operators) or 16.2.6 (if you have already migrated to 16.x). Verify with node -p "require('next/package.json').version". If you cannot patch within 24 hours, place a reverse proxy in front of the Next.js process that rejects HTTP request lines beginning with http:// / https://, enforce AWS IMDSv2 on every EC2 instance, and restrict the application server’s outbound traffic to cloud-metadata endpoints. Then patch anyway as soon as your dependency ecosystem (especially Nextra, next-auth, and similar plugins) catches up.
A note on the numbers. A few details in the public reporting are worth flagging:
- The original external reporter is not named in the public advisory (the CVE record’s
discoveryfield isUNKNOWN); Tim Neutkens at Vercel is the GHSA publisher, not necessarily the discoverer. - The “~79,000 exploitable hosts” figure is a Hadrian estimate derived from Shodan sampling, not a measured count.
- The CISA ADP SSVC entry records no observed exploitation in the wild as of the publication date — this is “high severity, broadly automatable, but not yet known to be mass-exploited,” not “actively exploited zero-day.”
- Some outlets used the word “critical” to describe CVE-2026-44578; the official CVSS rating is High (8.6), not Critical (≥9.0).
- Independent severity assessments differ slightly: GitHub/NVD score 8.6; Snyk scored it 7.7. Both are defensible CVSS computations.
Comments