nextjs - 💡(How to fix) Fix Standalone mode: NextResponse.rewrite() in middleware returns 307 instead of transparently serving rewritten content [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#91844Fetched 2026-04-08 01:20:41
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Timeline (top)
closed ×1commented ×1labeled ×1locked ×1

Root Cause

  • Even with a custom middleware that explicitly calls NextResponse.rewrite(url), the standalone server converts the rewrite into a 307 redirect.
  • Setting __NEXT_PRIVATE_ORIGIN=http://127.0.0.1:3100 does not fix the issue.
  • The bug affects ALL rewrite operations in middleware on standalone, not just next-intl. Any NextResponse.rewrite() call in middleware produces a 307 instead of a transparent rewrite.
  • When behind nginx with X-Forwarded-Proto: https, the rewrite URL becomes https://localhost:3100/en-US, causing EPROTO errors because the standalone server is HTTP-only.

Fix Action

Fix / Workaround

Current workaround

Code Example

HTTP/1.1 200 OK
x-middleware-rewrite: /en-US
[...page content...]

---

HTTP/1.1 307 Temporary Redirect
x-middleware-rewrite: http://localhost:3100/en-US
location: /

---

location / {
    set $locale_rewrite 1;
    if ($uri ~ "^/(en-US|es|fr|de|ja|ko)(/|$)") {
        set $locale_rewrite 0;
    }
    if ($locale_rewrite = 1) {
        rewrite ^/(.*)$ /en-US/$1 break;
    }
    proxy_pass http://127.0.0.1:3100;
}
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/jetbridge/next-middleware-repro (similar reproduction — standalone middleware rewrite bug)

To Reproduce

  1. Create a Next.js 16 app with output: 'standalone' in next.config.mjs
  2. Use next-intl (v4.8.3) middleware with localePrefix: 'as-needed' and defaultLocale: 'en-US'
  3. Build and run with the standalone server: node .next/standalone/server.js
  4. Place behind nginx with SSL termination (or test directly)
  5. Request GET /

Expected: The middleware rewrites / to /en-US internally and returns 200 with the homepage content.

Actual: The standalone server returns 307 Temporary Redirect with both location: / and x-middleware-rewrite: http://localhost:3100/en-US headers to the client, causing an infinite redirect loop.

Key finding

The same middleware code works correctly in next dev (returns 200 with rewritten content) but fails in the standalone production server.

In dev mode, the response is:

HTTP/1.1 200 OK
x-middleware-rewrite: /en-US
[...page content...]

In standalone mode, the response is:

HTTP/1.1 307 Temporary Redirect
x-middleware-rewrite: http://localhost:3100/en-US
location: /

The standalone server does not process the x-middleware-rewrite header internally — it sends the 307 + location to the client.

Additional details

  • Even with a custom middleware that explicitly calls NextResponse.rewrite(url), the standalone server converts the rewrite into a 307 redirect.
  • Setting __NEXT_PRIVATE_ORIGIN=http://127.0.0.1:3100 does not fix the issue.
  • The bug affects ALL rewrite operations in middleware on standalone, not just next-intl. Any NextResponse.rewrite() call in middleware produces a 307 instead of a transparent rewrite.
  • When behind nginx with X-Forwarded-Proto: https, the rewrite URL becomes https://localhost:3100/en-US, causing EPROTO errors because the standalone server is HTTP-only.

Current workaround

Disabled middleware rewrite and handle locale routing via nginx rewrite rules:

location / {
    set $locale_rewrite 1;
    if ($uri ~ "^/(en-US|es|fr|de|ja|ko)(/|$)") {
        set $locale_rewrite 0;
    }
    if ($locale_rewrite = 1) {
        rewrite ^/(.*)$ /en-US/$1 break;
    }
    proxy_pass http://127.0.0.1:3100;
}

Environment

Next.js16.2.1
Node.js24.14.0
OSUbuntu Linux (self-hosted)
BundlerTurbopack
DeployStandalone (output: 'standalone') behind nginx
next-intl4.8.3

Which area(s) are affected?

Middleware, Standalone mode, Routing

Which stage(s) are affected?

next build (production), standalone server

Additional context

This bug makes it impossible to use NextResponse.rewrite() in middleware when deploying with output: 'standalone'. The dev server handles rewrites correctly, but the standalone server always converts them to 307 redirects.

Related issues:

  • #55648 (307 redirect loop with locale + middleware, v13)
  • #84504 (forwarded server actions with middleware rewrites)
  • #54450 (x-forwarded-proto breaks redirects)

extent analysis

Fix Plan

To fix the issue with NextResponse.rewrite() in middleware when deploying with output: 'standalone', we need to modify the middleware to handle the rewrite internally instead of relying on the x-middleware-rewrite header.

Here are the steps:

  • Update next-intl middleware to handle rewrites internally:
import { NextResponse } from 'next/server';

export default async function middleware(req) {
  const url = new URL(req.url);
  const locale = 'en-US'; // default locale

  // Check if the URL already includes a locale
  if (!url.pathname.startsWith(`/${locale}`)) {
    // Handle the rewrite internally
    return NextResponse.rewrite(new URL(`/${locale}${url.pathname}`, req.url));
  }

  return NextResponse.next();
}

However, the above code will still cause a 307 redirect in standalone mode. To fix this, we need to use NextResponse.next() with the rewritten URL:

import { NextResponse } from 'next/server';

export default async function middleware(req) {
  const url = new URL(req.url);
  const locale = 'en-US'; // default locale

  // Check if the URL already includes a locale
  if (!url.pathname.startsWith(`/${locale}`)) {
    // Handle the rewrite internally
    const rewrittenUrl = new URL(`/${locale}${url.pathname}`, req.url);
    return NextResponse.next({ request: { url: rewrittenUrl } });
  }

  return NextResponse.next();
}
  • Update next.config.mjs to include the modified middleware:
module.exports = {
  //...
  experimental: {
    //...
    middleware: [(req) => middleware(req)],
  },
};

Verification

To verify that the fix worked, run the standalone server and request GET /. The response should be a 200 with the homepage content, and the URL should be rewritten to /en-US.

Extra Tips

  • Make sure to update next-intl to the latest version to ensure compatibility with the modified middleware.
  • If you're using a custom middleware, make sure to handle rewrites internally using NextResponse.next() with the rewritten URL.
  • If you're behind a reverse proxy like nginx, make sure to configure it to handle the rewritten URLs correctly.

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