vercel / vercel/next.js

createRedirectRenderResult fetch missing redirect: "manual" — causes errors and wasted latency on server action redirects

Open
#90,591 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Server Actions
Dominant language
JavaScript
Stars
142k
Forks
32.5k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/ajworkos/nextjs-redirect-bug-repro

To Reproduce
  1. Create a server action that calls redirect('/api/login') (or any internal route)
  2. The route handler at /api/login itself calls redirect('https://external-oauth-provider.com/authorize?...') — i.e., it redirects externally
  3. Wrap the server action in a client component <form action={...}> so it goes through the RSC action handler path
  4. Submit the form

Expected: The client receives the redirect and navigates to the external URL.

Actual (Demo 1 — error): If the external URL has a redirect chain (common with OAuth/SSO providers), the server-side fetch in createRedirectRenderResult follows all redirects and hits the "redirect count exceeded" limit, causing a 500 error.

Actual (Demo 2 — latency): Even when the chain is short enough to not error, the server unnecessarily fetches the external redirect target (e.g., a slow OAuth authorization page), adding seconds of wasted latency before the client gets the redirect response.

Current vs Expected behavior

Current: createRedirectRenderResult in packages/next/src/server/app-render/action-handler.ts makes a fetch() call without redirect: 'manual', causing it to automatically follow the entire redirect chain of the target URL server-side.

Expected: The fetch should use redirect: 'manual' (just like the sibling function createForwardedActionResponse already does), so the redirect is handed back to the client via the x-action-redirect header without following it server-side.

Root Cause

PR #65097 correctly added redirect: 'manual' to the createForwardedActionResponse function but missed applying the same fix to createRedirectRenderResult. The two functions serve similar roles (forwarding action responses), and the inconsistency means createRedirectRenderResult still follows redirects automatically.

The relevant code is at:

// createForwardedActionResponse — has the fix ✅
const response = await fetch(fetchUrl, {
  method: 'POST',
  body,
  duplex: 'half',
  headers: forwardedHeaders,
  redirect: 'manual',        // ← present
  next: { internal: 1 },
})

// createRedirectRenderResult — missing the fix ❌
const response = await fetch(fetchUrl, {
  method: 'GET',
  headers: forwardedHeaders,
                              // ← redirect: 'manual' missing
  next: { internal: 1 },
})
Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0
Binaries:
  Node: 20.17.0
  npm: 10.8.2
  pnpm: 9.15.0
Runtime Versions:
  next: 16.1.6
Which area(s) are affected? (Select all that apply)

Server Actions

Which stage(s) are affected? (Select all that apply)

next dev, next build (production), Deployed (Vercel or other)

Additional context

This is a one-line fix: add redirect: 'manual' to the fetch call in createRedirectRenderResult, matching what #65097 already did for createForwardedActionResponse.

I have a PR ready with the fix: #90137

The real-world impact is significant for any app where server actions redirect to route handlers that perform OAuth/SSO redirects (a very common pattern). We discovered this while upgrading a production auth application from Next.js 14 to 16. In v14, we had been carrying a workaround using relative URLs to avoid this codepath, but the underlying bug has existed since createRedirectRenderResult was introduced.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/next/src/server/app-render/action-handler.ts and compare createRedirectRenderResult with createForwardedActionResponse, which already uses manual redirects. Reproduce the redirect chain with the linked nextjs-redirect-bug-repro project, then verify that the external redirect is returned to the client without server-side follow-up or added latency.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, next.js
Domain
api, backend
Issue type
Bug
Difficulty
1/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.