为什么 Next.js 导航未按预期工作
发布: (2026年2月13日 GMT+8 05:44)
4 分钟阅读
原文: Dev.to
Source: Dev.to

我们遇到了一个问题:点击菜单项时,有时不会跳转到预期的页面。地址栏中的 URL 会正确变化,但屏幕上的内容却没有更新。反复点击菜单有时会让导航正常工作。该问题在本地开发环境中未出现,只在生产环境中出现。
下面是我们发现的原因以及解决办法。
原因
当点击 “ 时,Next.js 发送 RSC 请求
在 App Router 中,点击 “ 会触发客户端导航,而不是完整的页面重新加载。为了渲染下一页,Next.js 会发送一个与 React Server Components(RSC)相关的内部请求,以获取过渡所需的服务器渲染数据。这使得页面能够在不重新加载整个文档的情况下更新。
我们在网络面板中发现的内容
在导航期间检查网络面板时,我们看到以下响应头:
x-middleware-rewrite: /rate-limit-error?retryAfter=1&_rsc=xxxx
x-nextjs-rewritten-path: /rate-limit-error
x-middleware-rewrite– 中间件将内部 RSC 请求重写到rate-limit-error路由。x-nextjs-rewritten-path– Next.js 最终将请求处理为/rate-limit-error。
因此,内部 RSC 请求被重定向到限流错误页面。
问题流程
- 用户点击 “。
- Next.js 发送内部 RSC 请求以获取下一页的数据。
- 限流中间件错误地对该内部请求应用限流。
- 中间件将请求重写为
/rate-limit-error。 - 浏览器 URL 保持正确,但获取的内容来自不同的路由。
- 页面出现错误或未按预期更新。
让我们修复
之前
中间件的速率限制范围过宽,影响了内部的 Next.js 请求。
// Rate limit applied too broadly
const shouldRateLimit = true; // ← too broad
// Incomplete RSC detection
const hasRscParam = requestUrl.includes('_rsc=');
const hasRscHeader = request.headers.get('rsc') === '1';
const isRSCRequest = hasRscParam || hasRscHeader;
// Internal requests were not excluded
if (shouldRateLimit && isRateLimited) {
return NextResponse.rewrite(
new URL('/rate-limit-error', request.url)
);
}
之后
我们将速率限制仅限于 API 路由和非 GET 请求,并显式排除内部的 Next.js(RSC/路由)请求。
// Rate limit only APIs and non-GET requests
const isApiRoute = pathname.startsWith('/api/');
const isNonGetRequest = request.method !== 'GET';
const shouldRateLimit = isApiRoute || isNonGetRequest;
// Accurate RSC detection
const hasRscParam = requestUrl.includes('_rsc=');
const hasRscHeader = request.headers.get('rsc') === '1';
const hasRscAccept =
request.headers.get('accept')?.includes('text/x-component');
const isRSCRequest = hasRscParam || hasRscHeader || hasRscAccept;
// Exclude internal navigation requests
if (shouldRateLimit && !isRSCRequest && isRateLimited) {
return NextResponse.rewrite(
new URL('/rate-limit-error', request.url)
);
}
为避免将来出现类似问题
- 仅对 API 路由和非 GET 请求应用速率限制。
- 明确排除内部 Next.js 请求(如 RSC 和路由相关请求)不受中间件逻辑影响。
- 在中间件中使用重写时要谨慎,尤其是客户端导航。
- 当导航表现不一致时,检查 Network 面板和响应头部是否有意外的重写。
遵循这些做法有助于确保导航稳定,避免难以调试的生产问题。