Rewrites in middleware override next.config rewrites
Link to the code that reproduces this issue
https://codesandbox.io/p/sandbox/nextjs-middleware-rewrite-bug-wjlcns
To Reproduce
- Add rewrite rules in
next.config.jslike this:
const nextConfig = {
async rewrites() {
return [
{
source: "/paul/:slug",
destination: "https://paul.ren/api/:slug",
},
];
},
};
- Add rewrite rules in
middleware.jslike this:
import { NextResponse } from "next/server";
export function middleware(request) {
const nextUrl = request.nextUrl;
nextUrl.pathname = `/test/${nextUrl.pathname}`;
const response = NextResponse.rewrite(nextUrl);
return response;
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
- Open
projectUrl/paul/notein the browser, got 404 http code and err message.
Current vs. Expected behavior
Following the steps from the previous section, I expected to see the content of the proxied website normally, but i observed got a 404 error instead.
This is based on my reading of the relevant instructions in the middleware documentation, which has explicitly stated that the next.config.js has the highest priority.
https://nextjs.org/docs/app/building-your-application/routing/middleware#matching-paths
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 22.6.0: Wed Jul 5 22:17:35 PDT 2023; root:xnu-8796.141.3~6/RELEASE_ARM64_T8112
Available memory (MB): 24576
Available CPU cores: 8
Binaries:
Node: 20.8.0
npm: 10.1.0
Yarn: N/A
pnpm: 9.4.0
Relevant Packages:
next: 14.2.4 // Latest available version is detected (14.2.4).
eslint-config-next: 14.2.4
react: 18.3.1
react-dom: 18.3.1
typescript: 5.5.2
Next.js Config:
output: N/A
Which area(s) are affected? (Select all that apply)
Middleware
Which stage(s) are affected? (Select all that apply)
next dev (local)
Additional context
No response
The codes below are write in the middleware seems to achieve the result I need, but is it reasonable that the priority of middleware's rewrites is higher than next.config.js?
export function middleware(request) {
if (request.nextUrl.pathname.startsWith("/paul")) {
const response = NextResponse.rewrite(`https://paul.ren/${request.nextUrl.pathname.replace("/paul", "")}`);
return response;
}
...
}
This is based on my reading of the relevant instructions in the middleware documentation, which has explicitly stated that the next.config.js has the highest priority.
- headers from next.config.js
- redirects from next.config.js
- Middleware (rewrites, redirects, etc.)
- beforeFiles (rewrites) from next.config.js
- Filesystem routes (public/, _next/static/, pages/, app/, etc.)
- afterFiles (rewrites) from next.config.js
- Dynamic Routes (/blog/[slug])
- fallback (rewrites) from next.config.js
next.config.js rewrites are lower priority than middleware, so this is not a bug. It looks like it would work if you used redirect instead. Is that an option?
If using redirects isn't an option, you could also try changing your matcher config to exclude the /paul route:
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|paul).*)"],
next.config.jsrewrites are lower priority than middleware, so this is not a bug. It looks like it would work if you usedredirectinstead. Is that an option?
Sorry, it seems that I didn't read thoroughly enough. The next.config.js file only gives the highest priority to redirects, not the rewrites. However, I don't understand why they designed it this way. Could this be somewhat misleading? Although I found that using rewrites in middleware is actually more flexible than in next.config.js.
The fact that beforeFiles doesn't run before middleware files is a bit confusing.