Skip to content

proxy.js

注意:middleware 文件命名约定已弃用,并已重命名为 proxy。详细信息请参见 迁移到代理

¥Note: The middleware file convention is deprecated and has been renamed to proxy. See Migration to Proxy for more details.

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

¥The proxy.js|ts file is used to write Proxy 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.

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

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

需要了解:

¥Good to know:

代理应独立于渲染代码调用。在优化情况下,为了快速处理重定向/重写,代理会部署到 CDN。此时,你不应依赖共享模块或全局变量。

¥Proxy is meant to be invoked separately of your render code and in optimized cases deployed to your CDN for fast redirect/rewrite handling, you should not attempt relying on shared modules or globals.

要将信息从代理传递到你的应用,请使用 headerscookiesrewritesredirects 或 URL。

¥To pass information from Proxy to your application, use headers, cookies, rewrites, redirects, or the URL.

在项目根目录下创建 proxy.ts(或 .js)文件,或者如果适用,在 src 中创建,使其与 pagesapp 位于同一级别。

¥Create a proxy.ts (or .js) file in the project root, or inside src if applicable, so that it is located at the same level as pages or app.

如果你自定义了 pageExtensions,例如将其更改为 .page.ts.page.js,请相应地将文件命名为 proxy.page.tsproxy.page.js

¥If you’ve customized pageExtensions, for example to .page.ts or .page.js, name your file proxy.page.ts or proxy.page.js accordingly.

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

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

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

导出

¥Exports

代理函数

¥Proxy function

该文件必须导出单个函数,作为默认导出或命名为 proxy。请注意,不支持从同一文件中使用多个代理。

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

配置对象(可选)

¥Config object (optional)

可以选择将配置对象与代理函数一起导出。此对象包含 matcher,用于指定代理适用的路径。

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

匹配器

¥Matcher

matcher 选项允许你指定代理运行的目标路径。你可以通过多种方式指定这些路径:

¥The matcher option allows you to target specific paths for the Proxy 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 Proxy to both /about and /contact.

此外,matcher 选项支持使用正则表达式指定复杂的路径。例如,你可以使用正则表达式匹配器排除某些路径:

¥Additionally, the matcher option supports complex path specifications using regular expressions. For example, you can exclude certain paths with a regular expression matcher:

这支持精确控制要包含或排除的路径。

¥This enables 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.

  • 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*

  5. 锚定到路径的开头:/about 匹配 /about/about/team,但不匹配 /blog/about

    ¥Are anchored to the start of the path: /about matches /about and /about/team but not /blog/about

阅读有关 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 Proxy, 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 proxy(request: NextRequest) {
  // Proxy logic goes here
}

需要了解:

¥Good to know:

  • NextRequest 是 Next.js Proxy 中表示传入 HTTP 请求的类型,而 NextResponse 是一个用于操作和发送 HTTP 响应的类。

    ¥NextRequest is a type that represents incoming HTTP requests in Next.js Proxy, 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 Proxy, 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

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

¥Proxy 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 等)

    ¥Proxy (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

代理默认使用 Node.js 运行时。代理文件中不提供 runtime 配置选项。在代理中设置 runtime 配置选项会引发错误。

¥Proxy defaults to using the Node.js runtime. The runtime config option is not available in Proxy files. Setting the runtime config option in Proxy will throw an error.

高级代理标志

¥Advanced Proxy flags

在 Next.js 的 v13.1 中,为 Proxy 引入了两个额外的标志:skipMiddlewareUrlNormalizeskipTrailingSlashRedirect,以处理更高级的用例。

¥In v13.1 of Next.js two additional flags were introduced for proxy, 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 proxy 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 proxy(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 proxy(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 proxy(request: NextRequest) {
  // Clone the request headers and set a new header `x-hello-from-proxy1`
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-hello-from-proxy1', '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-proxy2`
  response.headers.set('x-hello-from-proxy2', 'hello')
  return response
}

请注意,此代码片段使用了:

¥Note that the snippet uses:

  • NextResponse.next({ request: { headers: requestHeaders } }) 用于使 requestHeaders 可在上游使用。

    ¥NextResponse.next({ request: { headers: requestHeaders } }) to make requestHeaders available upstream

  • 并非 NextResponse.next({ headers: requestHeaders }),而是 requestHeaders 可供客户使用

    ¥NOT NextResponse.next({ headers: requestHeaders }) which makes requestHeaders available to clients

代理中的 NextResponse 标头 中了解更多信息。

¥Learn more in NextResponse headers in Proxy.

需要了解:避免设置大标头,因为它可能会导致 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 Proxy 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 proxy(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 Proxy 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 proxy to paths starting with `/api/`
export const config = {
  matcher: '/api/:function*',
}

export function proxy(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 Proxy for certain requests by using the missing or has arrays, or a combination of both:

需要了解:

¥Good to know:

即使在否定匹配模式中排除了 _next/data,代理仍然会针对 _next/data 路由调用。这是一种有意为之的行为,旨在防止意外的安全问题,例如你可能保护了某个页面,但忘记保护相应的路由。

¥Even when _next/data is excluded in a negative matcher pattern, proxy will still be invoked for _next/data routes. This is intentional behavior to prevent accidental security issues where you might protect a page but forget to protect the corresponding data route.

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 Proxy 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 proxy(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 proxy files. Unit testing proxy can help ensure that it's only run on desired paths and that custom routing logic works as intended before code reaches production.

unstable_doesProxyMatch 函数可用于断言是否对提供的 URL、标头和 cookie 运行代理。

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

也可以测试整个代理函数。

¥The entire proxy function can also be tested.

平台支持

¥Platform support

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

了解如何在自托管 Next.js 时进行 配置代理

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

迁移到代理

¥Migration to Proxy

为何需要更改

¥Why the Change

重命名 middleware 的原因是术语 "中间件" 经常与 Express.js 中间件混淆,导致对其用途的误解。此外,中间件函数强大,因此建议使用;但是,建议仅在万不得已的情况下才使用此功能。

¥The reason behind the renaming of middleware is that the term "middleware" can often be confused with Express.js middleware, leading to a misinterpretation of its purpose. Also, Middleware is highly capable, so it may encourage the usage; however, this feature is recommended to be used as a last resort.

Next.js 致力于提供更符合人机工程学的 API,使开发者无需中间件即可实现目标。这就是 middleware 重命名的原因。

¥Next.js is moving forward to provide better APIs with better ergonomics so that developers can achieve their goals without Middleware. This is the reason behind the renaming of middleware.

为什么需要 "代理"

¥Why "Proxy"

名称“代理”明确了中间件的功能。术语 "proxy" 表示它在应用前面有一个网络边界,这是中间件的行为。此外,中间件默认在 Edge 运行时 上运行,它可以更靠近客户端,与应用区域分离。这些行为与 "proxy" 的术语更加吻合,并更清晰地说明了该功能的用途。

¥The name Proxy clarifies what Middleware is capable of. The term "proxy" implies that it has a network boundary in front of the app, which is the behavior of Middleware. Also, Middleware defaults to run at the Edge Runtime, which can run closer to the client, separated from the app's region. These behaviors align better with the term "proxy" and provide a clearer purpose of the feature.

如何迁移

¥How to Migrate

我们建议用户除非别无选择,否则避免依赖中间件。我们的目标是为他们提供更符合人机工程学的 API,以便他们无需中间件即可实现目标。

¥We recommend users avoid relying on Middleware unless no other options exist. Our goal is to give them APIs with better ergonomics so they can achieve their goals without Middleware.

术语“中间件”经常使用户将其与 Express.js 中间件混淆,这可能会导致误用。为了明确我们的方向,我们将文件命名约定重命名为“proxy”。这突显了我们正在摆脱中间件,拆分其功能过载的部分,并使代理的用途更加清晰。

¥The term “middleware” often confuses users with Express.js middleware, which can encourage misuse. To clarify our direction, we are renaming the file convention to “proxy.” This highlights that we are moving away from Middleware, breaking down its overloaded features, and making the Proxy clear in its purpose.

Next.js 提供了一个代码转换工具,用于从 middleware.ts 迁移到 proxy.ts。你可以运行以下命令进行迁移:

¥Next.js provides a codemod to migrate from middleware.ts to proxy.ts. You can run the following command to migrate:

bash
npx @next/codemod@canary middleware-to-proxy .

代码转换程序会将文件和函数名从 middleware 重命名为 proxy

¥The codemod will rename the file and the function name from middleware to proxy.

diff
// middleware.ts -> proxy.ts

- export function middleware() {
+ export function proxy() {

版本历史记录

¥Version history

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