nextjs - 💡(How to fix) Fix NextResponse.rewrite() breaks after first HMR update in dev (proxy/middleware edge runtime) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#90547Fetched 2026-04-08 00:19:54
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
0
Timeline (top)
labeled ×2closed ×1commented ×1locked ×1

Error Message

  1. Make the same request to /rewrite-me500 Internal Server Error

Root Cause

On the first request after a fresh server start, response instanceof Response is true. After HMR triggers, the same code path returns false because the Response class used by NextResponse and the one used by the runtime's instanceof check are now different objects (different module evaluation contexts).

Fix Action

Workaround

Using a plain Response with the internal x-middleware-rewrite header (which is what NextResponse.rewrite() does under the hood) avoids the class identity mismatch:

const newUrl = new URL(newPath, request.url);
return new Response(null, {
  headers: { "x-middleware-rewrite": newUrl.href },
});

This works because new Response() uses the global Response constructor rather than the one captured in the NextResponse module scope.

Code Example

import { NextRequest, NextResponse } from "next/server";

export async function proxy(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  if (pathname.startsWith("/rewrite-me")) {
    const newPath = pathname.replace("/rewrite-me", "/api/target");
    return NextResponse.rewrite(new URL(newPath, request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/rewrite-me/:path*"],
};

---

const newUrl = new URL(newPath, request.url);
return new Response(null, {
  headers: { "x-middleware-rewrite": newUrl.href },
});

---

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin 25.3.0

Binaries:
  Node: 22.14.0
  npm: 10.9.2

Relevant Packages:
  next: 16.1.6
  react: 19.1.0
  react-dom: 19.1.0
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/vercel/next.js/discussions

To Reproduce

  1. Create a proxy.ts (or middleware.ts in older versions) that rewrites a path:
import { NextRequest, NextResponse } from "next/server";

export async function proxy(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  if (pathname.startsWith("/rewrite-me")) {
    const newPath = pathname.replace("/rewrite-me", "/api/target");
    return NextResponse.rewrite(new URL(newPath, request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/rewrite-me/:path*"],
};
  1. Run next dev --turbopack (also reproduces without --turbopack, using webpack)
  2. Make a request to /rewrite-meworks correctly
  3. Edit any file to trigger HMR
  4. Make the same request to /rewrite-me500 Internal Server Error

Current vs. Expected behavior

Current: After HMR invalidates the edge runtime module, NextResponse.rewrite() returns a NextResponse instance that fails Next.js's internal response instanceof Response check. The rewrite silently fails and the request falls through to a 500.

On the first request after a fresh server start, response instanceof Response is true. After HMR triggers, the same code path returns false because the Response class used by NextResponse and the one used by the runtime's instanceof check are now different objects (different module evaluation contexts).

Expected: NextResponse.rewrite() should work consistently across HMR cycles in dev mode.

Workaround

Using a plain Response with the internal x-middleware-rewrite header (which is what NextResponse.rewrite() does under the hood) avoids the class identity mismatch:

const newUrl = new URL(newPath, request.url);
return new Response(null, {
  headers: { "x-middleware-rewrite": newUrl.href },
});

This works because new Response() uses the global Response constructor rather than the one captured in the NextResponse module scope.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin 25.3.0

Binaries:
  Node: 22.14.0
  npm: 10.9.2

Relevant Packages:
  next: 16.1.6
  react: 19.1.0
  react-dom: 19.1.0

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

Middleware / Proxy (Routing)

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

next dev (local)

Additional context

  • Reproduces with both --turbopack and webpack dev server
  • Does not affect production builds — only dev mode with HMR
  • The root cause appears to be that HMR re-evaluates the edge runtime module containing Response/NextResponse, but the instanceof check elsewhere uses the original class reference from before the reload

extent analysis

Quick Fix

Replace the NextResponse.rewrite() call with a plain Response that sets the x‑middleware-rewrite header. This uses the global Response constructor, so the instanceof Response check stays true after HMR.

// middleware.ts (or proxy.ts)
import { NextRequest } from "next/server";

export async function proxy(request: NextRequest) {
  const pathname = request.nextUrl.pathname;

  if (pathname.startsWith("/rewrite-me")) {
    // Build the target URL
    const

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - 💡(How to fix) Fix NextResponse.rewrite() breaks after first HMR update in dev (proxy/middleware edge runtime) [1 comments, 2 participants]