Skip to content

router.push() reuses cached redirect from prefetch without fresh middleware evaluation #88937

@KumarNitin19

Description

@KumarNitin19

Link to the code that reproduces this issue

https://github.com/KumarNitin19/nextjs-prefetch-redirect-bug

To Reproduce

I'm running middleware that conditionally redirects based on a cookie value. The / route is protected and redirects to /process when the processingComplete cookie is not set.

Steps to Reproduce:

  1. Navigate to /process page (no cookie set)
  2. Page renders with a sidebar containing <Link href="/">Dashboard</Link>
  3. Next.js automatically prefetches the / route
  4. Middleware evaluates and returns 307 redirect to /process (because cookie is false)
  5. Click "Start Process" button which:
    • Performs async work (simulated 2 seconds)
    • Sets cookie: processingComplete=true
    • Calls router.push('/') to navigate to dashboard
  6. Observe: User stuck on /process page, no navigation occurs

Live Demo (Vercel): https://nextjs-prefetch-redirect-bug.vercel.app/

Current vs. Expected behavior

Expected:
When router.push('/') is called after the cookie changes:

  • Fresh GET request should be made to /
  • Middleware should re-evaluate with new cookie value
  • Middleware should allow navigation (no redirect)
  • User navigates to Dashboard

Current:

  • No fresh request is made to /
  • Router reuses the cached redirect from initial prefetch
  • Middleware is never re-evaluated
  • User stuck on /process page

The router appears to cache the redirect response from the prefetch and reuses it on router.push() without checking if the state has changed.

Provide environment information

Relevant Packages:

  • next: 15.5.8 (also tested on 15.5.9, bug reproduces on both)
  • react: 19.1.0
  • react-dom: 19.1.0
  • typescript: ^5

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

Linking and Navigating, Middleware

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

Vercel (Deployed), Other (Deployed), next build (local)

Additional context

Workarounds:

  1. Disable prefetch (works reliably):
    <Link href="/" prefetch={false}>Dashboard</Link>

  2. Hard navigation (works reliably):
    window.location.href = '/';

  3. Router refresh before push (unreliable):

router.refresh();
await new Promise(resolve => setTimeout(resolve, 100));
router.push('/');

Impact:

This affects any route where NextResponse.redirect() was previously returned by middleware. Once the redirect is cached from a prefetch, subsequent router.push() calls to that route will reuse the cached redirect without re-evaluating middleware logic, even if the application state has changed.

Common scenarios affected:

  • Onboarding/signup flows with step-based redirects
  • Authentication state transitions
  • Feature flag-based routing

Metadata

Metadata

Assignees

No one assigned

    Labels

    Linking and NavigatingRelated to Next.js linking (e.g., <Link>) and navigation.MiddlewareRelated to Next.js Middleware.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions