# CVE-2026-44578: Next.js WebSocket SSRF

URL: https://blog.bokvi.com/blog/cve-2026-44578-nextjs-websocket-ssrf/
Published: 2026-05-15
Tags: security, nextjs, cve, ssrf, websocket

**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](https://github.com/vercel/next.js/security/advisories/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](https://hadrian.io/blog/next-js-websocket-ssrf-unauthenticated-access-to-internal-resources-cve-2026-44578-2) (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`](https://github.com/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](https://vercel.com/changelog/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`](https://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-subrequest` header. 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 (HTTP `upgrade` event, 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-proxy` request 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:

1. The **HTTP request handler** (for normal GET/POST/etc.).
2. The **HTTP upgrade handler** (for WebSocket upgrades — triggered by Node.js's `'upgrade'` event whenever it sees a `Connection: Upgrade` plus `Upgrade: <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)

```ts
// 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)

```ts
// 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:

```ts
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 `\`:

```ts
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:

```http
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:

1. Attacker opens a TCP connection to the public Next.js server on whatever port `next start` is listening on (default 3000, but irrelevant).
2. The request line uses an **absolute-form** URI pointing to the internal target (here AWS IMDSv1).
3. Node.js sees the `Connection: Upgrade` + `Upgrade: websocket` pair and fires its `'upgrade'` event instead of the normal `'request'` event.
4. Next.js's upgrade handler calls `resolveRoutes()`, gets back `parsedUrl.protocol = 'http:'` (because the request line was an absolute URI), and — without checking `finished` or `statusCode` — calls `proxyRequest()`.
5. `proxyRequest()` opens a TCP connection to `169.254.169.254:80` and forwards a GET to `/latest/meta-data/`.
6. 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.13` and `< 15.5.16`
- `>= 16.0.0` and `< 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`, or `output: standalone`, or `output: server`).
- The Next.js process must be reachable from the attacker (directly, or behind a reverse proxy that forwards absolute-form URIs and `Upgrade` headers 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 start` directly.
- 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`](https://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):**

```bash
# 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):**

```bash
# 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.16` **OR** `>= 16.0.0 < 16.2.5`.
- Recommended patched targets: **`>= 15.5.18`** on the 15.x track and **`>= 16.2.6`** on 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.js` by default unless explicitly disabled via `poweredByHeader: false`.
- **Build manifest:** `/_next/static/<buildId>/_buildManifest.js` is reachable on most exposed deployments and confirms a Next.js app.
- **Server header on `next start`:** Node.js's default `connection: keep-alive` and 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:

```bash
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 of `normalizeRepeatedSlashes` collapsing `http://` to `http:/`) 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://` or `https://`** (absolute-form URIs) — almost always anomalous and a strong exploit signature.
- The combination of an absolute-form URI *plus* `Upgrade: websocket` is 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 `AssumeRole` or unusual `GetObject` patterns 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 `next` versions 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:**

```bash
# 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:**

```bash
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):**

```bash
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+):**

```bash
yarn up next@16.2.6
yarn up react@19.1.7 react-dom@19.1.7
yarn install --immutable
yarn why next
```

**bun:**

```bash
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](https://nextjs.org/docs/app/guides/upgrading/version-16) guide — most relevant items:

- **Async Request APIs are mandatory in 16.x.** Synchronous access to `params`, `searchParams`, `cookies()`, `headers()`, and `draftMode()` was deprecated in 15 and **removed** in 16. Use `npx @next/codemod@latest next-async-request-api .` to migrate.
- **`middleware.js` is being renamed to `proxy.js`** in 16.x. The legacy `middleware` filename and the `middleware` named export are deprecated; the new `proxy` runtime is Node.js-only (the edge runtime continues to be supported under the legacy `middleware` name).
- **Image-metadata generators** now receive `params` and `id` as Promises.
- **Turbopack config** moved from `experimental.turbopack` to the top-level `turbopack` key.
- **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:

```nginx
# /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:

```caddyfile
@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:

```nginx
# 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:

```bash
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:

```bash
# 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://` or `https://`.
- 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

```bash
# 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 `@nextjs` X account, the `nextjs.org/blog` security feed, and GitHub repository security alerts for `vercel/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=required` everywhere** (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 start` instances in shadow IT.

## 5. References

### Primary / Authoritative Sources

- [GitHub Security Advisory (canonical) — GHSA-c4j6-fc7j-m34r](https://github.com/vercel/next.js/security/advisories/GHSA-c4j6-fc7j-m34r)
- [GitHub Advisory Database mirror](https://github.com/advisories/GHSA-c4j6-fc7j-m34r)
- [NVD entry — CVE-2026-44578](https://nvd.nist.gov/vuln/detail/CVE-2026-44578)
- [MITRE CVE record](https://www.cve.org/CVERecord?id=CVE-2026-44578)
- [Vercel May 2026 Security Release changelog](https://vercel.com/changelog/next-js-may-2026-security-release)
- [Next.js 15.5.16 release](https://github.com/vercel/next.js/releases/tag/v15.5.16)
- [Next.js 16.2.5 release](https://github.com/vercel/next.js/releases/tag/v16.2.5)
- [Next.js 15.5.18 / 16.2.6 follow-up announcement (Next.js X account)](https://x.com/nextjs/status/2052489312944759202)
- [Next.js security advisory index](https://github.com/vercel/next.js/security/advisories)

### Vulnerability Database Mirrors

- [Tenable](https://www.tenable.com/cve/CVE-2026-44578)
- [Snyk — SNYK-JS-NEXT-16638682](https://security.snyk.io/vuln/SNYK-JS-NEXT-16638682)
- [GitLab Advisory Database](https://advisories.gitlab.com/npm/next/CVE-2026-44578/)
- [OpenCVE](https://app.opencve.io/cve/CVE-2026-44578)
- [Feedly CVE](https://feedly.com/cve/CVE-2026-44578)
- [Intruder cvemon](https://cvemon.intruder.io/cves/CVE-2026-44578)

### In-Depth Technical Write-Ups

- [Hadrian — Next.js WebSocket SSRF (CVE-2026-44578)](https://hadrian.io/blog/next-js-websocket-ssrf-unauthenticated-access-to-internal-resources-cve-2026-44578-2) — May 13, 2026; the most technically detailed public write-up, including code diffs, the `normalizeRepeatedSlashes` analysis, and the Shodan-based exposure estimate of ~79,000 hosts.
- [Cybersecurity News — Multiple Critical Vulnerabilities Patched in Next.js and React Server Components](https://cybersecuritynews.com/next-js-react-server-vulnerabilities/) — 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](https://cybersecuritynews.com/next-js-vulnerability-exposes-credentials/) — May 15, 2026.
- [Cyberpress — Multiple Critical Vulnerabilities Patched in Next.js and React Server Components](https://cyberpress.org/vulnerabilities-patched-in-next-js-and-react/)
- [Cyberpress — Critical Next.js Flaw Exposes Cloud Credentials, API Keys, and Admin Panels](https://cyberpress.org/next-js-flaw-api-keys-admin-panels/)
- [GBHackers — Multiple Critical Flaws Fixed in Next.js and React Server Components](https://gbhackers.com/multiple-critical-flaws-fixed-in-next-js-and-react/)
- [TheHackerWire — Next.js SSRF via Crafted WebSocket Upgrade Requests](https://www.thehackerwire.com/next-js-ssrf-via-crafted-websocket-upgrade-requests-cve-2026-44578/)
- [Stork.AI — Next.js Security Alert: 13 New CVEs & Server Component Risks](https://www.stork.ai/blog/nextjs-the-security-meltdown)

### Proof-of-Concept Code

- [`dwisiswant0/next-16.2.4-pocs`](https://github.com/dwisiswant0/next-16.2.4-pocs) — comprehensive PoC collection for the entire May 2026 batch, including a working CVE-2026-44578 exploit (see `poc/CVE-2026-44578_GHSA-c4j6-fc7j-m34r/`). Authored by `dwi.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](https://projectdiscovery.io/blog/nextjs-middleware-authorization-bypass)
- [CVE-2025-29927 — Datadog Security Labs](https://securitylabs.datadoghq.com/articles/nextjs-middleware-auth-bypass/)
- [CVE-2025-29927 — Zscaler ThreatLabz](https://www.zscaler.com/blogs/security-research/cve-2025-29927-next-js-middleware-authorization-bypass-flaw)
- [CVE-2025-29927 — JFrog](https://jfrog.com/blog/cve-2025-29927-next-js-authorization-bypass/)
- [CVE-2025-29927 — OffSec](https://www.offsec.com/blog/cve-2025-29927/)
- [CVE-2025-29927 PoC (`zhero` / Rachid Allam original research)](https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware)
- [CVE-2026-23869 / 23870 (React Server Components DoS) — Vercel summary](https://vercel.com/changelog/summary-of-cve-2026-23869)

### Next.js Documentation and Upgrade Guides

- [Next.js v16 upgrade guide](https://nextjs.org/docs/app/guides/upgrading/version-16)
- [Next.js EOL / support schedule](https://eosl.date/eol/product/nextjs/)
- [Next.js blog (security & releases index)](https://nextjs.org/blog)

## 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 `discovery` field is `UNKNOWN`); 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.