NextJS生产环境中ISR页面访问时Middleware多次触发问题
Next.js生产环境下Middleware重复调用问题的排查与解决
我有一个NextJS应用,托管着从Supabase获取数据的ISR页面,配置了用于页面渲染前记录访问的Middleware,代码如下:
export async function middleware( request: NextRequest, fetchEvent: NextFetchEvent ) { console.log('request.nextUrl.pathname', request.nextUrl.pathname); if (request.nextUrl.pathname.split('/').length === 2) { return linkVisitMiddleware(request, fetchEvent); } }
问题是生产环境(next start)中该Middleware会被调用2-3次,服务器日志重复打印request.nextUrl.pathname /slug;但本地环境(next serve)下仅执行一次,符合预期。调整reactStrictMode无效,已在NextJS 13.1.1、13.1.6和13.3.0版本中测试过该问题。
可能的原因
- ISR重新验证触发额外请求:ISR页面在触发重新验证(revalidate)时,Next.js会发起后台请求重新生成页面,这会额外触发一次Middleware。如果访问时机刚好赶上页面重新验证周期,就会出现多次调用。
- 静态资源请求误匹配:生产环境中浏览器会自动请求
/favicon.ico、/robots.txt等资源,这些路径按split('/').length === 2的判断逻辑会被误识别为目标路由,导致Middleware重复执行。 - Next.js生产环境预取机制:生产环境下Next.js可能会对路由进行预取操作,预取请求也会触发Middleware。
解决方案
1. 精准匹配目标路由
放弃通过路径分割长度判断的方式,改用Next.js提供的matchPath工具函数精准匹配ISR页面路由(假设你的ISR页面路由为/[slug]):
import { matchPath } from 'next/router'; import { NextResponse } from 'next/server'; export async function middleware( request: NextRequest, fetchEvent: NextFetchEvent ) { console.log('request.nextUrl.pathname', request.nextUrl.pathname); // 仅匹配/[slug]格式的页面路由 if (matchPath('/[slug]', request.nextUrl.pathname)) { return linkVisitMiddleware(request, fetchEvent); } return NextResponse.next(); }
2. 排除静态资源与后台请求
在Middleware中添加过滤逻辑,排除静态资源、API请求以及ISR重新验证的后台请求:
import { NextResponse } from 'next/server'; export async function middleware( request: NextRequest, fetchEvent: NextFetchEvent ) { const pathname = request.nextUrl.pathname; console.log('request.nextUrl.pathname', pathname); // 排除静态资源、API、robots.txt等非页面请求 const excludeList = [ '/favicon.ico', '/robots.txt', '/sitemap.xml', '/api/', '/_next/' // 排除Next.js自身的静态资源路径 ]; if (excludeList.some(item => pathname.startsWith(item))) { return NextResponse.next(); } // 排除ISR重新验证的后台请求 if (request.headers.get('x-nextjs-revalidate')) { return NextResponse.next(); } if (pathname.split('/').length === 2) { return linkVisitMiddleware(request, fetchEvent); } return NextResponse.next(); }
3. 在记录逻辑中去重
如果上述方法仍无法解决,可以在linkVisitMiddleware中通过请求的唯一标识(比如用户IP+路径+时间戳)做去重处理,避免重复记录访问。
内容的提问来源于stack exchange,提问作者coding4fun
相关产品推荐
相关产品推荐

