在 Next.js 中,"Proxy" 这个概念实际上指向两个不同层面但同样重要的功能:一个是 Next.js 15 之后内置的、运行在边缘网络上的请求拦截器(原 Middleware) ,另一个则是通过配置文件实现的、用于转发 API 请求的 Rewrites 代理。
简单来说,前者像是应用的门卫 ,后者则像是应用的传话员。
为了帮你更清晰地理解,我把它们的核心区别整理成了一个表格:
| 特性 | 内置 proxy (原 Middleware) |
配置 rewrites (反向代理) |
|---|---|---|
| 核心定位 | 在请求到达页面之前,运行自定义代码进行拦截和处理。 | 将请求从一个路径"映射"到另一个路径(可以是内部或外部URL),对客户端透明。 |
| 主要用途 | 鉴权、A/B测试、国际化、日志记录、修改请求/响应头。 | 解决跨域问题、隐藏真实API地址、将旧站点路由平滑过渡到新应用、集成第三方服务。 |
| 运行时机 | 在 redirects 之后,beforeFiles rewrites 之前。 |
有三种时机:beforeFiles, afterFiles, fallback,可以在文件系统(页面/静态文件)检查前后执行。 |
| 代码位置 | 项目根目录下的 proxy.ts 或 proxy.js 文件。 |
next.config.js 文件中的 async rewrites() 函数。 |
| 能力范围 | 可以编程式地返回 NextResponse,实现 redirect, rewrite, 或直接响应。 |
声明式地配置 source 和 destination 映射关系,支持复杂的路径匹配规则。 |
深入理解:内置 proxy (原 Middleware)
从 Next.js 15 开始,原来的 middleware 文件被重命名为 proxy,其功能保持不变。你可以把它想象成一个在服务器上运行、在用户请求到达页面之前的"检查站"。
-
关键特性与能力 在
proxy函数中,你可以访问NextRequest对象,并通过返回NextResponse来实现多种操作:-
重写(Rewrite) :在服务端将请求映射到另一个 URL,但客户端浏览器地址栏的 URL 不会改变。这非常适合做 A/B 测试或在同一个域名下托管不同版本的应用。
typescript// proxy.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function proxy(request: NextRequest) { if (request.nextUrl.pathname.startsWith('/about')) { // 访问 /about 的用户,实际看到的是 /about-2 页面的内容 return NextResponse.rewrite(new URL('/about-2', request.url)) } } -
重定向(Redirect) :将请求引导至另一个 URL,浏览器地址栏会更新。适用于用户登录校验或永久移动的页面。
typescript// proxy.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function proxy(request: NextRequest) { if (!isLoggedIn(request)) { // 未登录用户访问 dashboard 时,跳转到登录页 return NextResponse.redirect(new URL('/login', request.url)) } } -
操作头信息与 Cookie:可以读取、设置或删除请求头和响应头,以及 Cookie,用于实现功能开关、用户追踪等。
-
直接响应 :甚至可以直接在
proxy层返回一个响应体,而不需要经过页面渲染,比如返回一个简单的robots.txt或维护页面。
-
-
配置与执行
proxy默认会作用于所有路由,因此强烈建议通过导出的config对象中的matcher选项来精确控制其生效路径,以优化性能。typescript// proxy.ts export const config = { matcher: [ /* * 匹配所有除了以这些开头的路径: * - api (API routes) * - _next/static (静态文件) * - _next/image (图片优化文件) * - favicon.ico, sitemap.xml (静态资源文件) */ '/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml).*)', ], }
深入理解:配置 rewrites (反向代理)
rewrites 是定义在 next.config.js 中的一个异步函数,它更像是一种"声明式"的代理规则。它告诉 Next.js 服务器:"当用户请求路径 A 时,你悄悄地替我去路径 B 拿到内容,然后返回给用户。"
-
核心价值与场景
-
解决跨域(CORS) :这是开发中最常见的用途。通过将前端对
/api的请求,在 Next.js 服务端转发到真实的后端 API 地址(如https://api.example.com),从而绕过了浏览器的同源策略。javascript// next.config.js module.exports = { async rewrites() { return [ { source: '/api/:path*', // 前端请求的路径 destination: 'https://api.example.com/:path*', // 实际转发的后端地址 }, ] }, } -
集成第三方服务:可以隐藏第三方服务的真实地址,将其"包装"成自己域名下的接口,避免被广告屏蔽器拦截,同时也更安全。
-
平滑迁移 :当你将旧站点逐步迁移到 Next.js 时,可以用
fallback类型的rewrites,让所有不存在的页面都 fallback 到旧站点,实现渐进式重构。
-
-
三种执行时机
rewrites函数可以返回一个对象,包含三种数组,它们在不同的时机执行,提供了极大的灵活性:beforeFiles:在检查public目录下的静态文件和页面路由之前执行。可以用来覆盖某些特定页面。afterFiles:在检查完静态文件和页面路由之后,但在动态路由(如pages/[slug].js)之前执行。fallback:在所有路由(包括动态路由)都没有匹配到之后,返回 404 页面之前执行。非常适合作为"最后一道防线"代理到旧系统。
-
高级匹配规则
rewrites的source路径支持参数化和正则表达式,并能根据has或missing条件(如特定的 header、cookie 或 query 参数)来决定是否应用。javascript// next.config.js module.exports = { async rewrites() { return [ { source: '/blog/:slug', destination: '/news/:slug', // 将 /blog/hello 转发到 /news/hello }, { source: '/old/:path*', destination: 'https://legacy-site.com/:path*', // 代理到外部 URL }, { source: '/admin/:path*', has: [{ type: 'cookie', key: 'authorized', value: 'true' }], destination: '/dashboard/:path*', // 只有携带特定 cookie 时才转发 }, ] }, }
总结与选择指南
简单来说,如何选择取决于你的业务逻辑:
- 如果你的逻辑是编程式的、条件复杂的、需要读取或修改请求/响应头 ,例如"检查用户是否登录,未登录就跳转",那么请使用
proxy。 - 如果你的逻辑是声明式的、纯粹的路径映射 ,例如"将所有
/api请求转发到另一个服务器来解决跨域问题",那么请使用rewrites。
在开发环境中,rewrites 通常是解决 API 跨域问题的最简单、最标准的方法。而在生产环境中,虽然 rewrites 也可以作为反向代理,但更常见的做法是在 Next.js 应用前面再加一层专业的反向代理服务器(如 Nginx),来处理负载均衡、SSL 终端和安全防护等更底层的网络问题。