Skip to content

middleware.js

middleware.js|ts 文件用于在请求完成之前写入 中间件 并在服务器上运行代码。然后,根据传入的请求,你可以通过重写、重定向、修改请求或响应标头或直接响应来修改响应。

¥The middleware.js|ts file is used to write Middleware and run code on the server before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.

中间件在渲染路由之前执行。它对于实现自定义服务器端逻辑(例如身份验证、日志记录或处理重定向)特别有用。

¥Middleware executes before routes are rendered. It's particularly useful for implementing custom server-side logic like authentication, logging, or handling redirects.

使用项目根目录中的文件 middleware.ts(或 .js)来定义中间件。例如,与 apppages 处于同一级别,或者在 src 内部(如果适用)。

¥Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as app or pages, or inside src if applicable.

tsx
import { NextResponse, NextRequest } from 'next/server'

// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
  return NextResponse.redirect(new URL('/home', request.url))
}

export const config = {
  matcher: '/about/:path*',
}

导出

¥Exports

中间件函数

¥Middleware function

该文件必须导出单个函数,作为默认导出或命名为 middleware。请注意,不支持来自同一文件的多个中间件。

¥The file must export a single function, either as a default export or named middleware. Note that multiple middleware from the same file are not supported.

配置对象(可选)

¥Config object (optional)

(可选)配置对象可以与中间件函数一起导出。该对象包括 matcher 以指定中间件应用的路径。

¥Optionally, a config object can be exported alongside the Middleware function. This object includes the matcher to specify paths where the Middleware applies.

匹配器

¥Matcher

matcher 选项允许你指定中间件运行的特定路径。你可以通过多种方式指定这些路径:

¥The matcher option allows you to target specific paths for the Middleware to run on. You can specify these paths in several ways:

  • 对于单个路径:直接用字符串定义路径,如 '/about'

    ¥For a single path: Directly use a string to define the path, like '/about'.

  • 对于多个路径:使用数组列出多个路径,例如 matcher: ['/about', '/contact'],它将中间件应用到 /about/contact

    ¥For multiple paths: Use an array to list multiple paths, such as matcher: ['/about', '/contact'], which applies the Middleware to both /about and /contact.

此外,matcher 选项支持通过正则表达式(例如 matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'])指定复杂的路径,从而可以精确控制要包含或排除的路径。

¥Additionally, the matcher option supports complex path specifications through regular expressions, such as matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'], enabling precise control over which paths to include or exclude.

matcher 选项接受一个包含以下键的对象数组:

¥The matcher option accepts an array of objects with the following keys:

  • source:用于匹配请求路径的路径或模式。它可以是用于直接路径匹配的字符串,也可以是用于更复杂匹配的模式。

    ¥source: The path or pattern used to match the request paths. It can be a string for direct path matching or a pattern for more complex matching.

  • regexp(可选):一个正则表达式字符串,根据源微调匹配。它提供了对包含或排除哪些路径的额外控制。

    ¥regexp (optional): A regular expression string that fine-tunes the matching based on the source. It provides additional control over which paths are included or excluded.

  • locale(可选):一个布尔值,当设置为 false 时,将忽略路径匹配中基于区域设置的路由。

    ¥locale (optional): A boolean that, when set to false, ignores locale-based routing in path matching.

  • has(可选):根据特定请求元素(例如标头、查询参数或 cookie)的存在来指定条件。

    ¥has (optional): Specifies conditions based on the presence of specific request elements such as headers, query parameters, or cookies.

  • missing(可选):重点关注缺少某些请求元素的情况,例如缺少标头或 cookie。

    ¥missing (optional): Focuses on conditions where certain request elements are absent, like missing headers or cookies.

配置的匹配器:

¥Configured matchers:

  1. 必须以 / 开头

    ¥MUST start with /

  2. 可以包含命名参数:/about/:path 匹配 /about/a/about/b,但不匹配 /about/a/c

    ¥Can include named parameters: /about/:path matches /about/a and /about/b but not /about/a/c

  3. 可以对命名参数进行修饰符(以 : 开头):/about/:path*/about/a/b/c 匹配,因为 * 为零或更大。? 为零或一,+ 为一或多个

    ¥Can have modifiers on named parameters (starting with :): /about/:path* matches /about/a/b/c because * is zero or more. ? is zero or one and + one or more

  4. 可以使用括号括起来的正则表达式:/about/(.*)/about/:path* 相同

    ¥Can use regular expression enclosed in parenthesis: /about/(.*) is the same as /about/:path*

阅读有关 path-to-regexp 文档的更多详细信息。

¥Read more details on path-to-regexp documentation.

需要了解:

¥Good to know:

  • matcher 值需要是常量,以便可以在构建时对其进行静态分析。诸如变量之类的动态值将被忽略。

    ¥The matcher values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored.

  • 为了向后兼容,Next.js 始终将 /public 视为 /public/index。因此,/public/:path 的匹配器将匹配。

    ¥For backward compatibility, Next.js always considers /public as /public/index. Therefore, a matcher of /public/:path will match.

参数

¥Params

request

定义中间件时,默认导出函数接受单个参数 request。该参数是 NextRequest 的实例,代表传入的 HTTP 请求。

¥When defining Middleware, the default export function accepts a single parameter, request. This parameter is an instance of NextRequest, which represents the incoming HTTP request.

tsx
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Middleware logic goes here
}

需要了解:

¥Good to know:

  • NextRequest 是代表 Next.js 中间件中传入 HTTP 请求的类型,而 NextResponse 是用于操作和发回 HTTP 响应的类。

    ¥NextRequest is a type that represents incoming HTTP requests in Next.js Middleware, whereas NextResponse is a class used to manipulate and send back HTTP responses.

NextResponse

NextResponse API 允许你:

¥The NextResponse API allows you to:

  • redirect 传入请求到不同的 URL

    ¥redirect the incoming request to a different URL

  • rewrite 通过显示给定 URL 进行响应

    ¥rewrite the response by displaying a given URL

  • 设置 API 路由、getServerSidePropsrewrite 目标的请求标头

    ¥Set request headers for API Routes, getServerSideProps, and rewrite destinations

  • 设置响应 cookie

    ¥Set response cookies

  • 设置响应标头

    ¥Set response headers

要从中间件生成响应,你可以:

¥To produce a response from Middleware, you can:

  1. rewrite 到产生响应的路由(页面Edge API 路由

    ¥rewrite to a route (Page or Edge API Route) that produces a response

  2. 直接返回 NextResponse。查看 产生响应

    ¥return a NextResponse directly. See Producing a Response

执行顺序

¥Execution order

将为项目中的每个路由调用中间件。鉴于此,使用 matchers 精准定位或排除特定路由至关重要。以下是执行顺序:

¥Middleware will be invoked for every route in your project. Given this, it's crucial to use matchers to precisely target or exclude specific routes. The following is the execution order:

  1. headersnext.config.js

    ¥headers from next.config.js

  2. redirectsnext.config.js

    ¥redirects from next.config.js

  3. 中间件(rewritesredirects 等)

    ¥Middleware (rewrites, redirects, etc.)

  4. beforeFilesrewrites)来自 next.config.js

    ¥beforeFiles (rewrites) from next.config.js

  5. 文件系统路由(public/_next/static/pages/app/ 等)

    ¥Filesystem routes (public/, _next/static/, pages/, app/, etc.)

  6. afterFilesrewrites)来自 next.config.js

    ¥afterFiles (rewrites) from next.config.js

  7. 动态路由 (/blog/[slug])

    ¥Dynamic Routes (/blog/[slug])

  8. fallbackrewrites)来自 next.config.js

    ¥fallback (rewrites) from next.config.js

运行时

¥Runtime

中间件默认使用 Edge 运行时。从 v15.2(canary)开始,我们对使用 Node.js 运行时提供了实验性支持。要启用,请将标志添加到你的 next.config 文件中:

¥Middleware defaults to using the Edge runtime. As of v15.2 (canary), we have experimental support for using the Node.js runtime. To enable, add the flag to your next.config file:

ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  experimental: {
    nodeMiddleware: true,
  },
}

export default nextConfig

然后在你的中间件文件中,将运行时设置为 config 对象中的 nodejs

¥Then in your middleware file, set the runtime to nodejs in the config object:

ts
export const config = {
  runtime: 'nodejs',
}

注意:此功能尚不推荐用于生产用途。因此,除非你使用的是 next@canary 版本而不是稳定版本,否则 Next.js 将抛出错误。

¥Note: This feature is not yet recommended for production use. Therefore, Next.js will throw an error unless you are using the next@canary release instead of the stable release.

高级中间件标志

¥Advanced Middleware flags

在 Next.js 的 v13.1 中,为中间件引入了两个附加标志 skipMiddlewareUrlNormalizeskipTrailingSlashRedirect 来处理高级用例。

¥In v13.1 of Next.js two additional flags were introduced for middleware, skipMiddlewareUrlNormalize and skipTrailingSlashRedirect to handle advanced use cases.

skipTrailingSlashRedirect 禁用 Next.js 重定向以添加或删除尾部斜杠。这允许中间件内部的自定义处理来维护某些路径的尾部斜杠,而不是其他路径,这可以使增量迁移更容易。

¥skipTrailingSlashRedirect disables Next.js redirects for adding or removing trailing slashes. This allows custom handling inside middleware to maintain the trailing slash for some paths but not others, which can make incremental migrations easier.

skipMiddlewareUrlNormalize 允许禁用 Next.js 中的 URL 规范化,以使直接访问和客户端转换的处理相同。在某些高级情况下,此选项通过使用原始 URL 提供完全控制。

¥skipMiddlewareUrlNormalize allows for disabling the URL normalization in Next.js to make handling direct visits and client-transitions the same. In some advanced cases, this option provides full control by using the original URL.

示例

¥Examples

条件语句

¥Conditional Statements

ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/about')) {
    return NextResponse.rewrite(new URL('/about-2', request.url))
  }

  if (request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.rewrite(new URL('/dashboard/user', request.url))
  }
}

使用 Cookie

¥Using Cookies

Cookie 是常规标头。在 Request 上,它们存储在 Cookie 标头中。在 Response 上,它们位于 Set-Cookie 标头中。Next.js 通过 NextRequestNextResponse 上的 cookies 扩展提供了一种便捷的方式来访问和操作这些 cookie。

¥Cookies are regular headers. On a Request, they are stored in the Cookie header. On a Response they are in the Set-Cookie header. Next.js provides a convenient way to access and manipulate these cookies through the cookies extension on NextRequest and NextResponse.

  1. 对于传入的请求,cookies 带有以下方法:getgetAllsetdelete cookie。你可以检查 has 的 cookie 是否存在,或者删除 clear 的所有 cookie。

    ¥For incoming requests, cookies comes with the following methods: get, getAll, set, and delete cookies. You can check for the existence of a cookie with has or remove all cookies with clear.

  2. 对于传出响应,cookies 有以下方法 getgetAllsetdelete

    ¥For outgoing responses, cookies have the following methods get, getAll, set, and delete.

ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Assume a "Cookie:nextjs=fast" header to be present on the incoming request
  // Getting cookies from the request using the `RequestCookies` API
  let cookie = request.cookies.get('nextjs')
  console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
  const allCookies = request.cookies.getAll()
  console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]

  request.cookies.has('nextjs') // => true
  request.cookies.delete('nextjs')
  request.cookies.has('nextjs') // => false

  // Setting cookies on the response using the `ResponseCookies` API
  const response = NextResponse.next()
  response.cookies.set('vercel', 'fast')
  response.cookies.set({
    name: 'vercel',
    value: 'fast',
    path: '/',
  })
  cookie = response.cookies.get('vercel')
  console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
  // The outgoing response will have a `Set-Cookie:vercel=fast;path=/` header.

  return response
}

设置标题

¥Setting Headers

你可以使用 NextResponse API 设置请求和响应标头(自 Next.js v13.0.0 起可以设置请求标头)。

¥You can set request and response headers using the NextResponse API (setting request headers is available since Next.js v13.0.0).

ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  // Clone the request headers and set a new header `x-hello-from-middleware1`
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-middleware1', 'hello')

  // You can also set request headers in NextResponse.next
  const response = NextResponse.next({
    request: {
      // New request headers
      headers: requestHeaders,
    },
  })

  // Set a new response header `x-hello-from-middleware2`
  response.headers.set('x-hello-from-middleware2', 'hello')
  return response
}

需要了解:避免设置大标头,因为它可能会导致 431 请求标头字段太大 错误,具体取决于你的后端 Web 服务器配置。

¥Good to know: Avoid setting large headers as it might cause 431 Request Header Fields Too Large error depending on your backend web server configuration.

CORS

你可以在中间件中设置 CORS 标头以允许跨域请求,包括 simplepreflighted 请求。

¥You can set CORS headers in Middleware to allow cross-origin requests, including simple and preflighted requests.

tsx
import { NextRequest, NextResponse } from 'next/server'

const allowedOrigins = ['https://acme.com', 'https://my-app.org']

const corsOptions = {
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export function middleware(request: NextRequest) {
  // Check the origin from the request
  const origin = request.headers.get('origin') ?? ''
  const isAllowedOrigin = allowedOrigins.includes(origin)

  // Handle preflighted requests
  const isPreflight = request.method === 'OPTIONS'

  if (isPreflight) {
    const preflightHeaders = {
      ...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
      ...corsOptions,
    }
    return NextResponse.json({}, { headers: preflightHeaders })
  }

  // Handle simple requests
  const response = NextResponse.next()

  if (isAllowedOrigin) {
    response.headers.set('Access-Control-Allow-Origin', origin)
  }

  Object.entries(corsOptions).forEach(([key, value]) => {
    response.headers.set(key, value)
  })

  return response
}

export const config = {
  matcher: '/api/:path*',
}

生成响应

¥Producing a response

你可以通过返回 ResponseNextResponse 实例直接从中间件进行响应。(从 Next.js v13.1.0 年开始可用)

¥You can respond from Middleware directly by returning a Response or NextResponse instance. (This is available since Next.js v13.1.0)

ts
import type { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'

// Limit the middleware to paths starting with `/api/`
export const config = {
  matcher: '/api/:function*',
}

export function middleware(request: NextRequest) {
  // Call our authentication function to check the request
  if (!isAuthenticated(request)) {
    // Respond with JSON indicating an error message
    return Response.json(
      { success: false, message: 'authentication failed' },
      { status: 401 }
    )
  }
}

否定匹配

¥Negative matching

matcher 配置允许完整的正则表达式,因此支持负向查找或字符匹配等匹配。可以在此处查看用于匹配除特定路径之外的所有路径的负前瞻示例:

¥The matcher config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here:

你还可以使用 missinghas 数组或两者的组合来绕过某些请求的中间件:

¥You can also bypass Middleware for certain requests by using the missing or has arrays, or a combination of both:

waitUntilNextFetchEvent

¥waitUntil and NextFetchEvent

NextFetchEvent 对象扩展了原生 FetchEvent 对象,并包含 waitUntil() 方法。

¥The NextFetchEvent object extends the native FetchEvent object, and includes the waitUntil() method.

waitUntil() 方法将 Promise 作为参数,并延长中间件的生命周期,直到 Promise 解决。这对于在后台执行工作很有用。

¥The waitUntil() method takes a promise as an argument, and extends the lifetime of the Middleware until the promise settles. This is useful for performing work in the background.

ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export function middleware(req: NextRequest, event: NextFetchEvent) {
  event.waitUntil(
    fetch('https://my-analytics-platform.com', {
      method: 'POST',
      body: JSON.stringify({ pathname: req.nextUrl.pathname }),
    })
  )

  return NextResponse.next()
}

单元测试(实验性)

¥Unit testing (experimental)

从 Next.js 15.1 开始,next/experimental/testing/server 包包含帮助对中间件文件进行单元测试的实用程序。单元测试中间件可以帮助确保它只在所需的路径上运行,并且自定义路由逻辑在代码投入生产之前按预期工作。

¥Starting in Next.js 15.1, the next/experimental/testing/server package contains utilities to help unit test middleware files. Unit testing middleware can help ensure that it's only run on desired paths and that custom routing logic works as intended before code reaches production.

unstable_doesMiddlewareMatch 函数可用于断言中间件是否会为提供的 URL、标头和 cookie 运行。

¥The unstable_doesMiddlewareMatch function can be used to assert whether middleware will run for the provided URL, headers, and cookies.

整个中间件函数也可以测试。

¥The entire middleware function can also be tested.

平台支持

¥Platform support

部署选项支持
Node.js 服务器
Docker 容器
静态导出
适配器平台相关

了解如何在自托管 Next.js 时进行 配置中间件

¥Learn how to configure Middleware when self-hosting Next.js.

版本历史记录

¥Version history

版本更改
v15.2.0中间件现在可以使用 Node.js 运行时(实验性)
v13.1.0添加了高级中间件标志
v13.0.0中间件可以修改请求头、响应头、发送响应
v12.2.0中间件稳定,请参见 升级指南
v12.0.9在 Edge Runtime 中强制使用绝对 URL (PR)
v12.0.0已添加中间件(测试版)