Sponsor
ntab.devntab.dev 提升效率的新标签页组件
点击查看
Menu

重定向

在 Next.js 中有几种处理重定向的方式。本页将介绍每种可用选项、使用场景以及如何管理大量重定向。

API用途使用位置状态码
redirect在变更或事件后重定向用户Server Components、Server Actions、Route Handlers307(临时)或 303(Server Action)
permanentRedirect在变更或事件后重定向用户Server Components、Server Actions、Route Handlers308(永久)
useRouter执行客户端导航Client Components 中的事件处理程序不适用
redirects in next.config.js基于路径重定向传入请求next.config.js 文件307(临时)或 308(永久)
NextResponse.redirect基于条件重定向传入请求Middleware任意

redirect 函数

redirect 函数允许你将用户重定向到另一个 URL。你可以在 Server ComponentsRoute HandlersServer Actions 中调用 redirect

redirect 通常在变更或事件后使用。例如,创建一篇文章后:

app/actions.ts
TypeScript
'use server'
 
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
 
export async function createPost(id: string) {
  try {
    // 调用数据库
  } catch (error) {
    // 处理错误
  }
 
  revalidatePath('/posts') // 更新缓存的文章
  redirect(`/post/${id}`) // 导航到新文章页面
}

值得注意的是

  • redirect 默认返回 307(临时重定向)状态码。当在 Server Action 中使用时,它返回 303(See Other)状态码,这通常用于在 POST 请求结果后重定向到成功页面。
  • redirect 内部会抛出错误,因此应该在 try/catch 块之外调用。
  • redirect 可以在 Client Components 的渲染过程中调用,但不能在事件处理程序中调用。你可以使用 useRouter 钩子 代替。
  • redirect 也接受绝对 URL,可用于重定向到外部链接。
  • 如果你想在渲染过程之前重定向,请使用 next.config.jsMiddleware

有关更多信息,请参阅 redirect API 参考

permanentRedirect 函数

permanentRedirect 函数允许你永久将用户重定向到另一个 URL。你可以在 Server ComponentsRoute HandlersServer Actions 中调用 permanentRedirect

permanentRedirect 通常在变更或改变实体的规范 URL 的事件后使用,例如用户更改用户名后更新其个人资料 URL:

app/actions.ts
TypeScript
'use server'
 
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
 
export async function updateUsername(username: string, formData: FormData) {
  try {
    // 调用数据库
  } catch (error) {
    // 处理错误
  }
 
  revalidateTag('username') // 更新对用户名的所有引用
  permanentRedirect(`/profile/${username}`) // 导航到新的用户资料
}

值得注意的是

  • permanentRedirect 默认返回 308(永久重定向)状态码。
  • permanentRedirect 也接受绝对 URL,可用于重定向到外部链接。
  • 如果你想在渲染过程之前重定向,请使用 next.config.jsMiddleware

有关更多信息,请参阅 permanentRedirect API 参考

useRouter() 钩子

如果你需要在 Client Component 的事件处理程序中重定向,可以使用 useRouter 钩子的 push 方法。例如:

app/page.tsx
TypeScript
'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

值得注意的是

  • 如果你不需要以编程方式导航用户,应该使用 <Link> 组件。

有关更多信息,请参阅 useRouter API 参考

redirectsnext.config.js

next.config.js 文件中的 redirects 选项允许你将传入的请求路径重定向到不同的目标路径。当你更改页面的 URL 结构或有一系列预先已知的重定向时,这很有用。

redirects 支持 路径匹配标头、Cookie 和查询匹配,使你能够根据传入请求灵活地重定向用户。

要使用 redirects,将该选项添加到你的 next.config.js 文件中:

next.config.ts
TypeScript
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  async redirects() {
    return [
      // 基本重定向
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // 通配符路径匹配
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}
 
export default nextConfig

有关更多信息,请参阅 redirects API 参考

值得注意的是

  • redirects 可以通过 permanent 选项返回 307(临时重定向)或 308(永久重定向)状态码。
  • redirects 在某些平台上可能有限制。例如,在 Vercel 上,限制为 1,024 个重定向。要管理大量重定向(1000+),可以考虑使用 Middleware 创建自定义解决方案。详见大规模管理重定向
  • redirects 在 Middleware 之前运行。

Middleware 中的 NextResponse.redirect

Middleware 允许你在请求完成之前运行代码。然后,根据传入的请求,使用 NextResponse.redirect 重定向到不同的 URL。当你想根据条件(如身份验证、会话管理等)重定向用户或有大量重定向时,这很有用。

例如,如果用户未通过身份验证,将其重定向到 /login 页面:

middleware.ts
TypeScript
import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
 
export function middleware(request: NextRequest) {
  const isAuthenticated = authenticate(request)
 
  // 如果用户已通过身份验证,正常继续
  if (isAuthenticated) {
    return NextResponse.next()
  }
 
  // 如果未通过身份验证,重定向到登录页面
  return NextResponse.redirect(new URL('/login', request.url))
}
 
export const config = {
  matcher: '/dashboard/:path*',
}

值得注意的是

  • Middleware 在 next.config.js 中的 redirects 之后运行,在渲染之前运行。

有关更多信息,请参阅 Middleware 文档。

大规模管理重定向(高级)

要管理大量重定向(1000+),你可以考虑使用 Middleware 创建自定义解决方案。这允许你以编程方式处理重定向,而无需重新部署应用程序。

要做到这一点,你需要考虑:

  1. 创建和存储重定向映射。
  2. 优化数据查找性能。

Next.js 示例:请参阅我们的 带有布隆过滤器的 Middleware 示例,了解下面建议的实现。

1. 创建和存储重定向映射

重定向映射是一个重定向列表,你可以将其存储在数据库(通常是键值存储)或 JSON 文件中。

考虑以下数据结构:

{
  "/old": {
    "destination": "/new",
    "permanent": true
  },
  "/blog/post-old": {
    "destination": "/blog/post-new",
    "permanent": true
  }
}

Middleware 中,你可以从数据库(如 Vercel 的 Edge ConfigRedis)读取,并根据传入请求重定向用户:

middleware.ts
TypeScript
import { NextResponse, NextRequest } from 'next/server'
import { get } from '@vercel/edge-config'
 
type RedirectEntry = {
  destination: string
  permanent: boolean
}
 
export async function middleware(request: NextRequest) {
  const pathname = request.nextUrl.pathname
  const redirectData = await get(pathname)
 
  if (redirectData && typeof redirectData === 'string') {
    const redirectEntry: RedirectEntry = JSON.parse(redirectData)
    const statusCode = redirectEntry.permanent ? 308 : 307
    return NextResponse.redirect(redirectEntry.destination, statusCode)
  }
 
  // 未找到重定向,继续而不重定向
  return NextResponse.next()
}

2. 优化数据查找性能

对每个传入请求读取大型数据集可能会很慢且成本高。你可以通过两种方式优化数据查找性能:

  • 使用针对快速读取进行优化的数据库,例如 Vercel Edge ConfigRedis
  • 使用数据查找策略,例如 布隆过滤器,在读取更大的重定向文件或数据库之前高效检查重定向是否存在。

考虑前面的示例,你可以将生成的布隆过滤器文件导入到 Middleware 中,然后检查传入请求的路径名是否存在于布隆过滤器中。

如果存在,将请求转发到 Route Handler ,它将检查实际文件并将用户重定向到适当的 URL。这避免了将大型重定向文件导入到 Middleware 中,这可能会减慢每个传入请求的速度。

middleware.ts
TypeScript
import { NextResponse, NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'
 
type RedirectEntry = {
  destination: string
  permanent: boolean
}
 
// 从生成的 JSON 文件初始化布隆过滤器
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
 
export async function middleware(request: NextRequest) {
  // 获取传入请求的路径
  const pathname = request.nextUrl.pathname
 
  // 检查路径是否在布隆过滤器中
  if (bloomFilter.has(pathname)) {
    // 将路径名转发到 Route Handler
    const api = new URL(
      `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
      request.nextUrl.origin
    )
 
    try {
      // 从 Route Handler 获取重定向数据
      const redirectData = await fetch(api)
 
      if (redirectData.ok) {
        const redirectEntry: RedirectEntry | undefined =
          await redirectData.json()
 
        if (redirectEntry) {
          // 确定状态码
          const statusCode = redirectEntry.permanent ? 308 : 307
 
          // 重定向到目标
          return NextResponse.redirect(redirectEntry.destination, statusCode)
        }
      }
    } catch (error) {
      console.error(error)
    }
  }
 
  // 未找到重定向,继续请求而不重定向
  return NextResponse.next()
}

然后,在 Route Handler 中:

app/api/redirects/route.ts
TypeScript
import { NextRequest, NextResponse } from 'next/server'
import redirects from '@/app/redirects/redirects.json'
 
type RedirectEntry = {
  destination: string
  permanent: boolean
}
 
export function GET(request: NextRequest) {
  const pathname = request.nextUrl.searchParams.get('pathname')
  if (!pathname) {
    return new Response('Bad Request', { status: 400 })
  }
 
  // 从 redirects.json 文件中获取重定向条目
  const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
 
  // 处理布隆过滤器误报
  if (!redirect) {
    return new Response('No redirect', { status: 400 })
  }
 
  // 返回重定向条目
  return NextResponse.json(redirect)
}

值得注意的是:

  • 要生成布隆过滤器,可以使用像 bloom-filters 这样的库。
  • 你应该验证对 Route Handler 的请求,以防止恶意请求。