NextJS 13.4 App Router中间件重定向后页面无样式问题求助
问题原因
你的中间件当前会拦截所有请求,包括页面加载所需的CSS、JS等静态资源(比如/_next/static/开头的文件)。当/sign-in页面尝试加载样式资源时,中间件会因为这些资源的路径不是/sign-in,将它们也重定向到/sign-in页面。这就导致样式文件无法正常加载,页面失去样式;同时,被重定向的静态资源请求会返回HTML内容,所以开发者工具Sources里会出现多个sign-in文档。
修复方案
需要让中间件仅拦截页面路由,排除静态资源、Next.js内部资源及API路由。你可以通过两种方式实现:
方式1:在中间件代码中排除路径
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { // 排除无需拦截的资源路径 const excludedPaths = ['/_next/', '/favicon.ico', '/api/'] const isExcluded = excludedPaths.some(path => req.nextUrl.pathname.startsWith(path)) // 仅对非排除路径且非登录页的请求重定向 if (!isExcluded && req.nextUrl.pathname !== '/sign-in') { return NextResponse.redirect(new URL('/sign-in', req.url)) } }
方式2:使用Next.js的matcher配置(更高效)
通过config里的matcher直接指定中间件作用的路径,避免不必要的拦截:
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { if (req.nextUrl.pathname !== '/sign-in') { return NextResponse.redirect(new URL('/sign-in', req.url)) } } // 匹配所有非API、非静态资源、非图片、非favicon的路径 export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], }
更优的中间件鉴权实现方式
针对生产环境的鉴权场景,建议优化以下几点:
- 精准控制拦截范围:用
matcher缩小中间件作用路径,只处理需要鉴权的页面路由。 - 会话校验逻辑:替换测试用的判断,加入实际的会话验证(比如从Cookie/Header获取令牌并校验有效性)。
- 避免无限重定向:区分公共页面(如登录、注册)和需要鉴权的页面,同时处理已登录用户访问登录页的跳转。
- 支持登录后回跳:保存用户原本访问的路径,登录成功后自动跳回。
示例代码:
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { // 1. 获取并校验会话(示例:从Cookie获取令牌,替换为实际校验逻辑) const sessionToken = req.cookies.get('session-token')?.value const isAuthenticated = !!sessionToken && await validateSessionToken(sessionToken) // 2. 定义无需鉴权的公共页面 const publicPaths = ['/sign-in', '/signup', '/forgot-password'] const isPublicPath = publicPaths.includes(req.nextUrl.pathname) // 3. 未认证且不在公共页面 → 重定向到登录页,携带回跳路径 if (!isAuthenticated && !isPublicPath) { const redirectUrl = new URL('/sign-in', req.url) redirectUrl.searchParams.set('redirectTo', req.nextUrl.pathname) return NextResponse.redirect(redirectUrl) } // 4. 已认证但在登录页 → 重定向到首页 if (isAuthenticated && req.nextUrl.pathname === '/sign-in') { return NextResponse.redirect(new URL('/', req.url)) } } // 示例:会话令牌校验函数(需自行实现) async function validateSessionToken(token: string) { // 比如调用API验证令牌有效性,或解码JWT检查过期时间 try { // 这里替换为实际校验逻辑 return true } catch (err) { return false } } // 仅匹配需要鉴权的页面路由 export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], }
内容的提问来源于stack exchange,提问作者Matthew Rideout
相关产品推荐
相关产品推荐

