September 9, 2026
Finding an SSRF in Next.js Server Actions
I was looking through the Next.js Server Action forwarding code when I noticed that X-Forwarded-Host could influence the host used for an…

By toast
1 min read
I was looking through the Next.js Server Action forwarding code when I noticed that X-Forwarded-Host could influence the host used for an internal request in custom server deployments.
That seemed worth testing.
The Host
In base-server.ts, Next.js preserves the X-Forwarded-Host header.
The internal IPC header filtering also didn't remove it.
Then in the Server Action forwarding code, parseHostHeader() could use that value when constructing the internal URL.
If __NEXT_PRIVATE_ORIGIN wasn't set, the host could fall back to the request headers.
So I had a way to control where Next.js would send the request.
Testing It
I set up a local Next.js instance and a small HTTP listener.
I triggered a Server Action, grabbed the action ID from the request, and replayed it with a controlled X-Forwarded-Host.
The listener received the request.
The request was coming from the Next.js server, not my browser.
It also contained the original request context, including the headers and body.
That confirmed the SSRF.
Reporting It
I reported it to Vercel through HackerOne in May 2026.
It was later marked as a duplicate, which had already been reported in November 2025 and was rated High (8.3).
So someone had already found it before me.
No bounty this time, but it was still a fun one to dig into.
One other thing I want to mention: My custom harness and skills assisted me during the research. I used it to help navigate the Next.js codebase and reason about the request flow. The reproduction and SSRF were then tested and verified locally by me.
Final Takeaway
The whole thing came down to X-Forwarded-Host making its way from the incoming request into an internal URL.
That was enough to make Next.js send a request to a host controlled by the attacker.