-
Notifications
You must be signed in to change notification settings - Fork 29.5k
Description
Link to the code that reproduces this issue
https://github.com/blairmcalpine/forwarded-server-action-request-loop-repro
To Reproduce
- npm i
- npm run dev
- Visit localhost:3000
- Click "run action" and then quickly (<500ms in between) click "Navigate to other page".
- Notice that (1) the server action fired by the "run action" button never resolves and (2) the server logs print out a ton of messages.
Current vs. Expected behavior
Current: When a server action gets forwarded (which happens when it fires after navigation to another page that doesn't know about that server action) and gets rewritten by middleware, it causes an infinite request loop.
Expected: Server action properly resolves and no request loop happens.
For full context, here is where this server action forwarding functionality was added.
In this case, the forwarding makes a new request to /rewrite/ from /rewrite/other, because /rewrite/ is the page that defines the server action. However when the server action makes it to middleware, it gets rewritten to /rewrite/rewrite and the process continues forever.
My suggestion for a solution would be (1): At a minimum, add a loop detection check using the x-action-forwarded header. If an incoming action has been forwarded already, don't forward it again no matter what.
A real solution though would likely look like making the forwarded server action request in the same format that the incoming one had. In this case, because the incoming request was made to /other and rewritten to /rewrite/other, the forwarded server request should not include the rewrite.
One can argue that you just shouldn't do a rewrite on these forwarded actions (which is what we've done at Faire to work around this issue), however this forwarded action behaviour is completely internal and undocumented, so it's not clear that anywhere that this would even cause that problem. Regardless of a solution here, I believe this behaviour should be documented as well. It can cause a lot of weirdness and problems in self-hosted setups and custom server wrappers especially where __NEXT_PRIVATE_ORIGIN is undefined, in which the server ends up making a request all the way back to the public domain.
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 24.6.0: Mon Aug 11 21:15:09 PDT 2025; root:xnu-11417.140.69.701.11~1/RELEASE_ARM64_T6041
Available memory (MB): 49152
Available CPU cores: 14
Binaries:
Node: 22.15.1
npm: 10.9.2
Yarn: 1.22.22
pnpm: N/A
Relevant Packages:
next: 15.5.4 // Latest available version is detected (15.5.4).
eslint-config-next: 15.5.4
react: 19.1.0
react-dom: 19.1.0
typescript: 5.9.3
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Server Actions, Middleware
Which stage(s) are affected? (Select all that apply)
next dev (local), next start (local), Other (Deployed)
Additional context
No response