Redirecting 重定向

There are a few ways you can handle redirects in Next.js. This page will go through each available option, use cases, and how to manage large numbers of redirects.

在 Next.js 中,有几种方法可以处理重定向。此页面将介绍每个可用选项、用例以及如何管理大量重定向。

API

Purpose 

Where

Status Code

redirect

Redirect user after a mutation or event
在发生变更或事件后重定向用户

Server Components, Server Actions, Route Handlers
服务器组件、服务器操作、路由处理程序

307 (Temporary) or 303 (Server Action)
307(临时)或 303(服务器操作)

permanentRedirect

Redirect user after a mutation or event
在发生变更或事件后重定向用户

Server Components, Server Actions, Route Handlers
服务器组件、服务器操作、路由处理程序

308 (Permanent) 308(永久)

useRouter

Perform a client-side navigation
执行客户端导航

Event Handlers in Client Components
客户端组件中的事件处理程序

N/A

redirects in next.config.js  redirects 中的 next.config.js

Redirect an incoming request based on a path
根据路径重定向传入的请求

next.config.js file  next.config.js 文件

307 (Temporary) or 308 (Permanent)
307(临时)或 308(永久)

NextResponse.redirect

Redirect an incoming request based on a condition
根据条件重定向传入请求

Middleware 中间件

Any

1.redirect function redirect 函数

The redirect function allows you to redirect the user to another URL. You can call redirect in Server Components, Route Handlers, and Server Actions.

使用 redirect 函数可以将用户重定向到另一个 URL。您可以在服务器组件、路由处理程序和服务器操作中调用 redirect 。

redirect is often used after a mutation or event. For example, creating a post:

redirect 通常在发生突变或事件后使用。例如,创建帖子:

'use server'
 
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
 
export async function createPost(id: string) {
  try {
    // Call database
  } catch (error) {
    // Handle errors
  }
 
  revalidatePath('/posts') // Update cached posts
  redirect(`/post/${id}`) // Navigate to the new post page
}

Good to know: 值得了解:

  • redirect returns a 307 (Temporary Redirect) status code by default. When used in a Server Action, it returns a 303 (See Other), which is commonly used for redirecting to a success page as a result of a POST request.
    redirect 默认返回 307(临时重定向)状态代码。当在服务器操作中使用时,它返回 303(另请参阅),通常用于在 POST 请求的结果中重定向到成功页面。

  • redirect internally throws an error so it should be called outside of try/catch blocks.
    redirect 内部引发错误,因此应在 try/catch 块之外调用它。

  • redirect can be called in Client Components during the rendering process but not in event handlers. You can use the useRouter hook instead.
    redirect 可以在渲染过程中在客户端组件中调用,但不能在事件处理程序中调用。您可以改用 useRouter 钩子。

  • redirect also accepts absolute URLs and can be used to redirect to external links.
    redirect 也接受绝对 URL,可用于重定向到外部链接。

  • If you'd like to redirect before the render process, use next.config.js or Middleware.
    如果您想在渲染过程之前重定向,请使用 next.config.js 或中间件。

2.permanentRedirect function permanentRedirect 函数

The permanentRedirect function allows you to permanently redirect the user to another URL. You can call permanentRedirect in Server Components, Route Handlers, and Server Actions.

使用 permanentRedirect 函数可以将用户永久重定向到另一个 URL。您可以在服务器组件、路由处理程序和服务器操作中调用 permanentRedirect 。

permanentRedirect is often used after a mutation or event that changes an entity's canonical URL, such as updating a user's profile URL after they change their username:

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

'use server'
 
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
 
export async function updateUsername(username: string, formData: FormData) {
  try {
    // Call database
  } catch (error) {
    // Handle errors
  }
 
  revalidateTag('username') // Update all references to the username
  permanentRedirect(`/profile/${username}`) // Navigate to the new user profile
}

Good to know: 值得了解:

  • permanentRedirect returns a 308 (permanent redirect) status code by default.
    permanentRedirect 默认返回 308(永久重定向)状态代码。

  • permanentRedirect also accepts absolute URLs and can be used to redirect to external links.
    permanentRedirect 也接受绝对 URL,可用于重定向到外部链接。

  • If you'd like to redirect before the render process, use next.config.js or Middleware.
    如果您想在渲染过程之前重定向,请使用 next.config.js 或中间件。

3.useRouter() hook useRouter() 钩子

If you need to redirect inside an event handler in a Client Component, you can use the push method from the useRouter hook. For example:
如果您需要在客户端组件的事件处理程序中进行重定向,则可以使用 useRouter 钩子中的 push 方法。例如:

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

4.redirects in next.config.js redirects 在 next.config.js 中

The redirects option in the next.config.js file allows you to redirect an incoming request path to a different destination path. This is useful when you change the URL structure of pages or have a list of redirects that are known ahead of time.

redirects 选项在 next.config.js 文件中允许您将传入的请求路径重定向到不同的目标路径。当您更改页面的 URL 结构或提前知道重定向列表时,这非常有用。

redirects supports path, header, cookie, and query matching, giving you the flexibility to redirect users based on an incoming request.

redirects 支持路径、标头、Cookie 和查询匹配,让您可以根据传入的请求灵活地重定向用户。

To use redirects, add the option to your next.config.js file:

中使用 redirects ,请将选项添加到 next.config.js 文件:

module.exports = {
  async redirects() {
    return [
      // Basic redirect
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // Wildcard path matching
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}

Good to know: 值得了解:

  • redirects can return a 307 (Temporary Redirect) or 308 (Permanent Redirect) status code with the permanent option.
    redirects 可以使用 permanent 选项返回 307(临时重定向)或 308(永久重定向)状态代码。

  • redirects may have a limit on platforms. For example, on Vercel, there's a limit of 1,024 redirects. To manage a large number of redirects (1000+), consider creating a custom solution using Middleware. See managing redirects at scale for more.
    redirects 在平台上可能有限制。例如,在 Vercel 上,重定向的限制为 1,024 个。要管理大量重定向(1000 个以上),请考虑使用中间件创建自定义解决方案。有关更多信息,请参阅大规模管理重定向。

  • redirects runs before Middleware.
    redirects 在中间件之前运行。

5.NextResponse.redirect in Middleware 中间件中的NextResponse.redirect

Middleware allows you to run code before a request is completed. Then, based on the incoming request, redirect to a different URL using NextResponse.redirect. This is useful if you want to redirect users based on a condition (e.g. authentication, session management, etc) or have a large number of redirects.

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

For example, to redirect the user to a /login page if they are not authenticated:

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

import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
 
export function middleware(request: NextRequest) {
  const isAuthenticated = authenticate(request)
 
  // If the user is authenticated, continue as normal
  if (isAuthenticated) {
    return NextResponse.next()
  }
 
  // Redirect to login page if not authenticated
  return NextResponse.redirect(new URL('/login', request.url))
}
 
export const config = {
  matcher: '/dashboard/:path*',
}

Good to know: 值得了解:

  • Middleware runs after redirects in next.config.js and before rendering.
    中间件在 next.config.js 中的 redirects 之后和渲染之前运行。

6.Managing redirects at scale (advanced) 大规模管理重定向(高级)

To manage a large number of redirects (1000+), you may consider creating a custom solution using Middleware. This allows you to handle redirects programmatically without having to redeploy your application.

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

To do this, you'll need to consider:

为此,您需要考虑:

Creating and storing a redirect map.

创建和存储重定向映射。

Optimizing data lookup performance.

优化数据查找性能。

a. Creating and storing a redirect map 创建和存储重定向映射

A redirect map is a list of redirects that you can store in a database (usually a key-value store) or JSON file.

重定向映射是您可以存储在数据库(通常是键值存储)或 JSON 文件中的重定向列表。

Consider the following data structure:

考虑以下数据结构:

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

In Middleware, you can read from a database such as Vercel's Edge Config or Redis, and redirect the user based on the incoming request:

在 Middleware 中,您可以从数据库(例如 Vercel 的 Edge Config 或 Redis)中读取,并根据传入的请求重定向用户:

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)
  }
 
  // No redirect found, continue without redirecting
  return NextResponse.next()
}

b.Optimizing data lookup performance 优化数据查找性能

Reading a large dataset for every incoming request can be slow and expensive. There are two ways you can optimize data lookup performance:

为每个传入请求读取大型数据集可能会很慢且昂贵。您可以通过两种方式优化数据查找性能:

-Use a database that is optimized for fast reads, such as Vercel Edge Config or Redis.

使用针对快速读取进行优化的数据库,例如 Vercel Edge Config 或 Redis。

-Use a data lookup strategy such as a Bloom filter to efficiently check if a redirect exists before reading the larger redirects file or database.

使用数据查找策略(例如布隆过滤器)在读取较大的重定向文件或数据库之前有效检查重定向是否存在。

Considering the previous example, you can import a generated bloom filter file into Middleware, then, check if the incoming request pathname exists in the bloom filter.

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

If it does, forward the request to a Route Handler which will check the actual file and redirect the user to the appropriate URL. This avoids importing a large redirects file into Middleware, which can slow down every incoming request.

如果存在,则将请求转发到将检查实际文件并将用户重定向到适当 URL 的路由处理程序。这避免了将大型重定向文件导入到中间件中,这可能会减慢每个传入请求的速度。

import { NextResponse, NextRequest } from 'next/server'
import { ScalableBloomFilter } from 'bloom-filters'
import GeneratedBloomFilter from './redirects/bloom-filter.json'
 
type RedirectEntry = {
  destination: string
  permanent: boolean
}
 
// Initialize bloom filter from a generated JSON file
const bloomFilter = ScalableBloomFilter.fromJSON(GeneratedBloomFilter as any)
 
export async function middleware(request: NextRequest) {
  // Get the path for the incoming request
  const pathname = request.nextUrl.pathname
 
  // Check if the path is in the bloom filter
  if (bloomFilter.has(pathname)) {
    // Forward the pathname to the Route Handler
    const api = new URL(
      `/api/redirects?pathname=${encodeURIComponent(request.nextUrl.pathname)}`,
      request.nextUrl.origin
    )
 
    try {
      // Fetch redirect data from the Route Handler
      const redirectData = await fetch(api)
 
      if (redirectData.ok) {
        const redirectEntry: RedirectEntry | undefined =
          await redirectData.json()
 
        if (redirectEntry) {
          // Determine the status code
          const statusCode = redirectEntry.permanent ? 308 : 307
 
          // Redirect to the destination
          return NextResponse.redirect(redirectEntry.destination, statusCode)
        }
      }
    } catch (error) {
      console.error(error)
    }
  }
 
  // No redirect found, continue the request without redirecting
  return NextResponse.next()
}

Then, in the Route Handler:

然后,在路由处理程序中:

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 })
  }
 
  // Get the redirect entry from the redirects.json file
  const redirect = (redirects as Record<string, RedirectEntry>)[pathname]
 
  // Account for bloom filter false positives
  if (!redirect) {
    return new Response('No redirect', { status: 400 })
  }
 
  // Return the redirect entry
  return NextResponse.json(redirect)
}

Good to know: 值得了解:

  • To generate a bloom filter, you can use a library like bloom-filters.
    要生成布隆过滤器,您可以使用像 bloom-filters 这样的库。

  • You should validate requests made to your Route Handler to prevent malicious requests.
    您应该验证对路由处理程序发出的请求,以防止恶意请求。