You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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).*)'],
}
更优的中间件鉴权实现方式

针对生产环境的鉴权场景,建议优化以下几点:

  1. 精准控制拦截范围:用matcher缩小中间件作用路径,只处理需要鉴权的页面路由。
  2. 会话校验逻辑:替换测试用的判断,加入实际的会话验证(比如从Cookie/Header获取令牌并校验有效性)。
  3. 避免无限重定向:区分公共页面(如登录、注册)和需要鉴权的页面,同时处理已登录用户访问登录页的跳转。
  4. 支持登录后回跳:保存用户原本访问的路径,登录成功后自动跳回。

示例代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 08:03:30