"好的重定向,是用户体验与 SEO 的隐形守护者。"
在现代 Web 应用中,重定向(Redirect)不仅是页面跳转的技术手段,更是保障 SEO 连续性、维护用户会话、实现权限控制 的关键机制。Next.js 提供了五种不同层级的重定向能力,覆盖从静态配置到动态逻辑的全场景需求。
本文将系统梳理 Next.js 中的重定向方案,帮助你精准选择合适的方式,避免常见陷阱。
一、五种重定向方式概览
| 方法 | 适用场景 | 执行时机 | 状态码 | 位置 |
|---|---|---|---|---|
redirect() |
表单提交后跳转、服务端事件响应 | 渲染过程中 | 307(临时) / 303(Server Action) | Server Component / Server Action / Route Handler |
permanentRedirect() |
URL 永久变更(如用户名修改) | 渲染过程中 | 308(永久) | Server Component / Server Action / Route Handler |
useRouter().push() |
客户端交互跳转(按钮点击等) | 浏览器事件中 | ---(客户端导航) | Client Component 事件处理器 |
redirects in next.config.js |
静态路由迁移、已知路径重写 | 请求进入时 | 307 / 308 | 构建时配置 |
NextResponse.redirect() |
动态条件跳转(如登录拦截) | Middleware 中 | 任意 | Middleware |
✅ 核心原则:
- 服务端跳转 → 用
redirect/permanentRedirect/next.config.js/ Middleware- 客户端跳转 → 用
useRouter
二、服务端重定向:redirect 与 permanentRedirect
2.1 临时重定向:redirect()
适用于表单提交后跳转、创建资源后导航等场景:
ts
// app/actions.ts
'use server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
// 1. 创建文章(伪代码)
const id = await db.post.create({ title })
// 2. 更新缓存
revalidatePath('/posts')
// 3. 跳转到新文章页
redirect(`/posts/${id}`) // ← 307 临时重定向
}
⚠️ 注意:
redirect()会抛出异常,不要放在try/catch内部- 在 Server Action 中返回 303 See Other(符合 POST 后跳转规范)
2.2 永久重定向:permanentRedirect()
适用于URL 永久变更,如用户修改用户名后旧链接失效:
ts
// app/actions.ts
'use server'
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
export async function updateUsername(newUsername: string) {
await db.user.update({ username: newUsername })
revalidateTag('profile')
permanentRedirect(`/profile/${newUsername}`) // ← 308 永久重定向
}
✅ SEO 友好:搜索引擎会将旧 URL 的权重转移到新 URL。
三、客户端跳转:useRouter
用于用户交互触发的导航,如按钮点击、表单提交(非 Server Action):
tsx
// app/components/GoToDashboard.tsx
'use client'
import { useRouter } from 'next/navigation'
export default function GoToDashboard() {
const router = useRouter()
return (
<button onClick={() => router.push('/dashboard')}>
进入控制台
</button>
)
}
✅ 最佳实践:
- 非交互式跳转优先用
<Link>(支持预加载)- 仅在事件处理器中使用
useRouter
四、静态重定向:next.config.js 中的 redirects
适用于已知的、静态的路径映射,如网站改版、旧博客迁移:
js
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
async redirects() {
return [
// 基础重定向
{
source: '/about',
destination: '/',
permanent: true, // 308
},
// 通配符匹配
{
source: '/blog/:slug',
destination: '/news/:slug',
permanent: true,
},
// 带查询参数
{
source: '/old-search',
destination: '/search?q=legacy',
permanent: false, // 307
},
]
},
}
export default nextConfig
⚠️ 限制:
- Vercel 平台最多支持 1024 条
redirects- 超过此数量需改用 Middleware + 数据库
五、动态重定向:Middleware 中的 NextResponse.redirect
适用于基于用户状态的条件跳转,如未登录用户访问受保护页面:
ts
// middleware.ts
import { NextResponse, NextRequest } from 'next/server'
import { getAuth } from '@/lib/auth'
export function middleware(request: NextRequest) {
const isAuthenticated = getAuth(request)
if (!isAuthenticated && request.nextUrl.pathname.startsWith('/dashboard')) {
// 重定向到登录页,保留原路径
const url = new URL('/login', request.url)
url.searchParams.set('from', request.nextUrl.pathname)
return NextResponse.redirect(url)
}
return NextResponse.next()
}
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'],
}
✅ 优势:
- 支持任意状态码
- 可访问 cookies、headers、session
- 执行早于页面渲染
六、大规模重定向:1000+ 路由迁移方案
当重定向规则超过 1000 条(如 CMS 迁移),推荐 Middleware + Bloom Filter 方案:
步骤 1:构建重定向映射(JSON 或数据库)
json
// redirects.json
{
"/old-post-1": { "destination": "/news/post-1", "permanent": true },
"/legacy/page": { "destination": "/new/page", "permanent": false }
}
步骤 2:Middleware 中使用 Bloom Filter 快速过滤
ts
// middleware.ts
import { ScalableBloomFilter } from 'bloom-filters'
import bloomData from './bloom-filter.json'
const bloom = ScalableBloomFilter.fromJSON(bloomData)
export async function middleware(request: NextRequest) {
const path = request.nextUrl.pathname
if (bloom.has(path)) {
// 可能存在重定向,交由 API 验证
const res = await fetch(`/api/redirect?path=${encodeURIComponent(path)}`)
if (res.ok) {
const { destination, permanent } = await res.json()
return NextResponse.redirect(destination, permanent ? 308 : 307)
}
}
return NextResponse.next()
}
步骤 3:Route Handler 返回真实重定向
ts
// app/api/redirect/route.ts
import redirects from '@/redirects.json'
export async function GET(request: NextRequest) {
const path = request.nextUrl.searchParams.get('path')
if (!path || !redirects[path]) {
return new Response(null, { status: 404 })
}
return Response.json(redirects[path])
}
✅ 性能优势:
- Bloom Filter 内存占用小,误判率可控
- 避免将全部重定向规则加载到 Middleware
七、常见陷阱与最佳实践
❌ 错误:在 try/catch 内调用 redirect
ts
// 危险!redirect 会被 catch 捕获,导致跳转失效
try {
await createPost()
redirect('/success') // ❌ 不会执行
} catch (e) {
// ...
}
✅ 正确做法 :将 redirect 放在 try 外部
ts
await createPost()
redirect('/success') // ✅
❌ 错误:在 Client Component 渲染时调用 redirect
tsx
// app/page.tsx
export default function Page() {
redirect('/login') // ❌ 仅限 Server Component
}
✅ 正确做法 :服务端逻辑放 Server Component,客户端跳转用 useRouter
✅ 最佳实践总结
- SEO 重定向 → 用
next.config.js或permanentRedirect - 权限控制 → 用 Middleware
- 表单提交后跳转 → 用 Server Action +
redirect - 用户交互跳转 → 用
useRouter - 大规模迁移 → 用 Middleware + Bloom Filter
结语
Next.js 的重定向体系体现了其"渐进式复杂度"的设计哲学:
- 简单场景 → 一行配置搞定
- 复杂场景 → 灵活组合 Middleware、Server Action 与缓存机制
掌握这五种重定向方式,你不仅能优雅处理页面跳转,更能构建健壮、可维护、SEO 友好的现代 Web 应用。
记住:重定向不是"绕路",而是为用户和搜索引擎铺设的"正确路径"。