为什么 Next.js 导航未按预期工作

发布: (2026年2月13日 GMT+8 05:44)
4 分钟阅读
原文: Dev.to

Source: Dev.to

关于为何 Next.js 导航未按预期工作 的封面图片

我们遇到了一个问题:点击菜单项时,有时不会跳转到预期的页面。地址栏中的 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 请求被重定向到限流错误页面。

问题流程

  1. 用户点击 “。
  2. Next.js 发送内部 RSC 请求以获取下一页的数据。
  3. 限流中间件错误地对该内部请求应用限流。
  4. 中间件将请求重写为 /rate-limit-error
  5. 浏览器 URL 保持正确,但获取的内容来自不同的路由。
  6. 页面出现错误或未按预期更新。

让我们修复

之前

中间件的速率限制范围过宽,影响了内部的 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 面板和响应头部是否有意外的重写。

遵循这些做法有助于确保导航稳定,避免难以调试的生产问题。

0 浏览
Back to Blog

相关文章

阅读更多 »

疯狂的 React key

通过 map 渲染 tsx export function Parent { const array, setArray = useState1, 2, 3, 4, 5; useEffect => { setTimeout => { setArrayprev => 6, 7, 8, 9, 10, ...prev;...