Skip to content

Forwarded server actions with middleware rewrites cause an infinite request loop #84504

@blairmcalpine

Description

@blairmcalpine

Link to the code that reproduces this issue

https://github.com/blairmcalpine/forwarded-server-action-request-loop-repro

To Reproduce

  1. npm i
  2. npm run dev
  3. Visit localhost:3000
  4. Click "run action" and then quickly (<500ms in between) click "Navigate to other page".
  5. 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

NEXT-4736

Metadata

Metadata

Assignees

No one assigned

    Labels

    MiddlewareRelated to Next.js Middleware.Server ActionsRelated to Server Actions.linear: nextConfirmed issue that is tracked by the Next.js team.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions