Next.js CVE-2026-44578: WebSocket SSRF Exposes 79,000 Self-Hosted Servers

CVE-2026-44578 is an unauthenticated SSRF in the Next.js WebSocket upgrade handler. It affects self-hosted Next.js 13.4.13 through 16.2.4 and lets attackers reach cloud metadata services, internal admin panels, and any host on port 80. Here is the technical root cause, how to patch, and how to harden the cloud environment around it.

13 min read·

TL;DR

Key Point Summary
What happened An unauthenticated server-side request forgery (SSRF) vulnerability was disclosed in the Next.js WebSocket upgrade handler. Self-hosted Next.js servers can be coerced into making internal HTTP GET requests to any host on port 80 reachable from the server.
CVE CVE-2026-44578, GHSA-c4j6-fc7j-m34r, CVSS 8.6 (High)
Vulnerable versions Next.js 13.4.13 and later, all 14.x, 15.x before 15.5.16, and 16.0.0 before 16.2.4
Fixed versions 15.5.16 and 16.2.5
Scope Self-hosted Next.js deployments only. Applications hosted on Vercel are not affected.
Root cause The WebSocket upgrade handler in router-server.ts called proxyRequest without checking the finished flag returned by route resolution. A request line containing an absolute URI like GET http://169.254.169.254/ HTTP/1.1 was forwarded verbatim.
Real-world exposure Roughly 79,000 directly-exposed self-hosted Next.js servers running vulnerable versions are exploitable without reverse-proxy protection.
Action Timeline
Upgrade Next.js to 15.5.16 or 16.2.5 on every self-hosted deployment Immediately
Enforce IMDSv2 with hop-limit 1 on every cloud instance running Next.js This week
Add a WAF or reverse-proxy rule rejecting request URIs that begin with http:// or https:// 48 to 72 hours
Audit IAM roles attached to Next.js hosts and restrict egress to private IP ranges and metadata endpoints This sprint

If you run Next.js outside of Vercel, treat this as a same-day patch. The proof-of-concept is public, the exploit is trivial (a single curl request), and the most valuable target on the network, the cloud metadata service, sits behind a single unauthenticated HTTP GET.


What Is CVE-2026-44578?

CVE-2026-44578 is an unauthenticated server-side request forgery (SSRF) vulnerability in the Next.js standalone server. It was disclosed on May 11, 2026 by Hadrian Security and assigned a CVSS score of 8.6 (High).

The flaw lives in the part of Next.js that upgrades HTTP connections to WebSocket connections. By sending a single crafted HTTP request that combines a WebSocket upgrade header with an absolute-form URI, an attacker can convince the Next.js process to proxy the request to any host and port on the internal network or the cloud metadata endpoint, then return the response.

There are three properties that make this vulnerability particularly serious:

  1. No authentication required. The exploit works against any internet-exposed Next.js endpoint, regardless of which routes are protected.
  2. No path restriction. The attacker controls the host, port, and path of the proxied request.
  3. The most useful target is one HTTP GET away. The AWS Instance Metadata Service, the Google Cloud metadata server, and the Azure Instance Metadata Service all live at 169.254.169.254 on port 80.

CVE-2026-44578 is the latest in a pattern of SSRF flaws turning into cloud credential disclosure. The same primitive class compromised LMDeploy inference servers in April, which we covered in LMDeploy CVE-2026-33626.

Who Is Affected

The vulnerability affects self-hosted Next.js deployments in the following version ranges:

  • Next.js 13.4.13 through 13.x (latest)
  • All Next.js 14.x versions
  • Next.js 15.x before 15.5.16
  • Next.js 16.0.0 before 16.2.4

The fix landed in 15.5.16 and 16.2.5. Both releases backport the same patch to the WebSocket upgrade handler.

Vercel-hosted Next.js applications are not affected. Vercel terminates connections at its edge, normalizes the request URI, and does not expose the Next.js WebSocket upgrade handler in a way that makes the attack reachable. The 79,000 vulnerable hosts identified by Hadrian are all self-hosted, typically running behind a load balancer or directly on a cloud instance.

If you are unsure how your Next.js application is deployed:

  • If your team runs next start on EC2, ECS, EKS, GCE, GKE, AKS, Fly.io, Render, Railway, or any VPS, you are self-hosted and likely in scope.
  • If you deployed by connecting a GitHub repository to Vercel and never managed the runtime, you are not in scope.
  • If you use the standalone output mode (output: 'standalone') and run the result behind your own infrastructure, you are self-hosted.

The Technical Root Cause

The vulnerable code lives in packages/next/src/server/lib/router-server.ts, in the WebSocket upgrade handler. The handler receives an HTTP request that has the Upgrade: websocket header and is responsible for either rejecting it or proxying it to the appropriate internal handler.

Before the patch, the handler destructured only two values from the route resolver:

TS
const { matchedOutput, parsedUrl } = await resolveRoutes(...)

if (parsedUrl.protocol) {
  return await proxyRequest(req, socket, parsedUrl, head)
}

The bug is in what is missing. The HTTP request handler in the same file checks an additional value, finished, returned by resolveRoutes. The finished flag indicates that routing has actually approved this request for proxying. The WebSocket handler never read it.

That meant any request whose parsed URL had a non-empty protocol field, regardless of whether routing approved it, was forwarded to proxyRequest.

The exploit takes advantage of HTTP request line parsing in Node.js. A standard HTTP request uses an origin-form URI:

Text
GET /api/health HTTP/1.1
Host: example.com

But the HTTP spec also allows absolute-form URIs in the request line, traditionally used for HTTP proxies:

Text
GET http://169.254.169.254/latest/meta-data/ HTTP/1.1
Host: example.com

Node.js's HTTP parser preserves this verbatim in req.url. When Next.js calls url.parse(req.url), parsedUrl.protocol is "http:". The vulnerable handler sees a truthy protocol, skips the finished check that does not exist, and proxies the request.

The patch is small and correct. It mirrors the validation logic already present in the HTTP request handler:

TS
const { matchedOutput, parsedUrl, finished, statusCode } = await resolveRoutes(...)

if (finished && parsedUrl.protocol) {
  if (!statusCode) {
    return await proxyRequest(req, socket, parsedUrl, head)
  }
  return socket.end()
}

Proof of Concept

The exploit is one HTTP request. No special tooling required.

HTTP
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: vulnerable-app.example.com
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

The vulnerable Next.js server receives this request, parses the URL as http://169.254.169.254/latest/meta-data/iam/security-credentials/, recognizes the WebSocket upgrade headers, fails to check the finished flag, and proxies the request to the AWS Instance Metadata Service. The response, which on an IMDSv1 host includes the IAM role name and shortly thereafter the temporary credentials, is returned to the attacker.

The same primitive works against:

  • http://169.254.169.254/ for cloud metadata (AWS, GCP, Azure)
  • http://localhost:6379/ for Redis bound to loopback
  • http://10.0.0.5:8080/admin for internal admin endpoints in the same VPC
  • http://kubernetes.default.svc/ for the Kubernetes API server from inside a pod

The one limitation in the SSRF primitive is that it is a GET on port 80. That is more than enough to extract credentials, read internal HTTP endpoints, and confirm reachability of internal services for further attack planning.

Why This Vulnerability Class Keeps Winning

CVE-2026-44578 is, mechanically, the same shape as the LMDeploy SSRF we analysed in April. A server-side process accepts a user-supplied URL or URI, fails to validate that the destination is external, and forwards the request to whatever was asked for. The pattern has been understood by application security teams for over a decade. It keeps appearing in new places because:

  1. Frameworks are large and have many request entry points. Next.js has request handling for HTTP, WebSocket upgrades, static assets, middleware, image optimisation, and rewrites. Each entry point needs the same validation logic. Forgetting one is enough.
  2. Cloud environments expose a single high-value endpoint on a fixed address. The metadata service is the most valuable secret in most cloud accounts and it is gated only by network reachability. Any process on the host can read it. Any SSRF on the host can read it.
  3. Default IAM roles are too broad. When the metadata service is reachable, the question is no longer "is the role compromised" but "what does the role grant."

The structural fix is not just to patch this CVE. It is to assume that another SSRF will land in another part of your stack and make sure that, when it does, it cannot reach the metadata service or escalate to a broad IAM role.

What to Do Right Now

1. Patch Next.js

Upgrade to 15.5.16 or 16.2.5 on every self-hosted deployment. Treat this as a same-day patch. The exploit is public, trivial, and unauthenticated.

Bash
# 15.x branch
npm install next@15.5.16

# 16.x branch
npm install next@16.2.5

If you cannot upgrade immediately, the next two controls reduce the blast radius significantly.

2. Block absolute-form request URIs at the edge

Deploy a reverse proxy, WAF, or CDN rule that rejects any HTTP request line where the path starts with http:// or https://. nginx is a common choice:

Nginx
# Reject absolute-form request URIs before they reach Next.js
if ($request_uri ~* "^https?://") {
    return 400;
}

Cloudflare, Fastly, AWS WAF, and similar services all support equivalent rules. This control alone neutralises CVE-2026-44578 even on unpatched Next.js versions.

3. Enforce IMDSv2 with hop-limit 1

This is the highest-leverage control you can deploy across your fleet. It defeats most SSRF-to-credential-disclosure attacks on AWS, regardless of which framework or application is vulnerable.

Bash
aws ec2 modify-instance-metadata-options \
  --instance-id i-xxxxxxxxxxxxxxxxx \
  --http-tokens required \
  --http-put-response-hop-limit 1 \
  --http-endpoint enabled

For new instances, set the same options in your launch template, Terraform module, or CloudFormation template. We covered the broader baseline in our CIS Benchmarks for AWS guide, and the same principle applies on Azure and GCP. The metadata endpoint should not be reachable from a single unauthenticated HTTP GET.

4. Restrict egress from Next.js hosts

The minimum egress policy for a Next.js host:

  • Deny outbound to all RFC 1918 private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) except for explicit internal services that must be reached
  • Deny outbound to link-local addresses (169.254.0.0/16), which includes the metadata service
  • Allow outbound only to specific external domains the application actually needs

This policy stops CVE-2026-44578 from being useful even on a fully unpatched server.

5. Audit detection for the exploitation signature

Add the following indicators to your SIEM, runtime detection, or log analysis pipeline:

  • Application logs containing the string Failed to proxy http:/ (the normalisation fingerprint Next.js writes when this path is hit)
  • Reverse-proxy access logs showing request URIs starting with http:// or https://, especially when combined with Upgrade: websocket
  • Outbound connections from Next.js hosts to 169.254.169.254:80
  • AWS CloudTrail events showing IAM credentials used from a source IP that is not the instance itself

If you have a SOAR or runtime tool, build an alert on the combination of WebSocket upgrade headers and an absolute-form request URI. It is a clean signal with no legitimate use case.

Compliance Implications

If you are pursuing SOC 2, ISO 27001, or Cyber Essentials Plus, an unpatched CVE of this severity is auditor-relevant. Specific controls auditors will ask about:

  • CC6.1 (logical access security): Are IAM roles on Next.js hosts least-privilege? If the metadata service is reachable, that question becomes "what does the role grant on disclosure."
  • CC6.6 (boundary protection): How do you protect against threats from outside the system boundary? Is the metadata service reachable from a user-influenced HTTP request? Is egress filtered?
  • CC7.1 (vulnerability management): How did you detect CVE-2026-44578? What was your time-to-patch? Auditors increasingly expect SLA-based patching for high-severity CVEs on internet-exposed services.
  • CC7.2 (system monitoring): Do you detect anomalous traffic patterns from Next.js hosts, including outbound traffic to private IP ranges or metadata endpoints?
  • A.5.7 (ISO 27001:2022, threat intelligence): How are you tracking CVEs in the frameworks you depend on? Is there a documented process for ingesting GHSA advisories?

We covered the 2026 SOC 2 expectations in detail in what changed in SOC 2 for 2026. Patching cadence and boundary protection are recurring themes.

The Bigger Picture: Next.js Is Now Critical Infrastructure

Next.js powers a meaningful share of B2B SaaS applications, particularly in the seed-to-Series-B segment that we work with at Bastion. When a framework reaches that level of adoption, individual CVEs in it become incidents at a fleet level. 79,000 directly-exposed servers is not a long tail. It is a substantial fraction of self-hosted Next.js deployments worldwide.

There are two takeaways for security and platform teams:

  1. Subscribe to GHSA advisories for every framework in your dependency graph. Next.js publishes security advisories through GitHub Security Advisories. The same applies to React, Node.js, Express, Fastify, NestJS, and any framework your application is built on. Time-to-patch starts when the advisory drops, not when you happen to read about it.
  2. Defence in depth is not optional when the framework is the entry point. A WAF or reverse proxy in front of Next.js is no longer a nice-to-have. It is the layer that catches the next CVE that has not been disclosed yet. The same is true for IMDSv2, egress filtering, and IAM scoping.

Vercel-hosted applications got most of these defences by default. Self-hosted applications inherit responsibility for all of them.

Conclusion

CVE-2026-44578 is a textbook unauthenticated SSRF in one of the most widely deployed web frameworks. The patch is small, the exploit is one HTTP request, and the most valuable target is the cloud metadata service. The structural mitigations, IMDSv2, egress filtering, scoped IAM roles, edge filtering of absolute-form URIs, are the same controls that defeat the next SSRF before it is even disclosed.

If you would like help auditing your Next.js attack surface, scoping IAM roles on cloud workloads, or mapping these controls to a SOC 2 or ISO 27001 audit, get in touch with our team.


Sources

Share this article

Other platforms check the box

We secure the box

Get in touch and learn why hundreds of companies trust Bastion to manage their security and fast-track their compliance.

Get Started