Next.js Proxy (取代 Middleware)
在 Next.js 的演進過程中,Request 攔截層曾經歷過多次變革。從早期的 _middleware 到 middleware.ts,而在 Next.js 16 之後,官方正式推出了 proxy.ts 來取代舊有的 Middleware 機制。
這次改動不僅是更名,更是為了明確區分「代理 (Proxy)」與「後端邏輯」的邊界,並解決了舊版 Middleware 可能造成的安全性隱患 (CVE-2025-29927)。
為什麼要從 Middleware 變成 Proxy?
- 安全性 (Security):
middleware.ts過去容易因為設定不當(例如 Regex 寫錯),導致惡意請求繞過驗證直接打進 API。proxy.ts採用更嚴格的 "Proxy-first" 設計。 - 語意明確 (Semantics):舊名稱容易讓開發者誤以為它是 Express 裡的 Middleware(可以做繁重的運算),但實際上它運行在 Edge Runtime,應該只負責輕量的 路由、轉址、標頭處理。
proxy.ts的命名清楚地傳達了它的職責:它是應用程式的大門。
基本設定 (Setup)
proxy.ts 必須放在專案的根目錄下(如果是使用 src 目錄則是 src/proxy.ts),與 app 或 pages 同層級。
基礎語法
我們使用新的 nextProxy 函式來定義處裡邏輯:
// src/proxy.ts
import { nextProxy } from 'next/server';
export default nextProxy({
async handle(request) {
console.log('收到請求:', request.nextUrl.pathname);
// 放行請求,繼續往後端送
return nextProxy.next();
},
});
// 定義匹配路徑 (Matcher)
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
核心 API 操作 (API Operations)
nextProxy 的設計核心在於 Request -> Processing -> Response 的流水線處理。根據你想要修改的是「發出去的請求」還是「回傳的回應」,寫法會有所不同。
A. 修改請求 (Request Modification)
如果你想要在請求發送給後端 API 或 Page 之前,注入一些資訊(例如:使用者所在的國家代碼),你需要修改 Request Headers。
關鍵語法:透過 nextProxy.next({ request: { headers } }) 傳遞修改後的標頭。
// src/proxy.ts
import { nextProxy } from 'next/server';
export default nextProxy({
async handle(request) {
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-user-country', 'TW');
// � 將修改後的 Headers "往下傳" 給後端
return nextProxy.next({
request: {
headers: requestHeaders,
},
});
},
});
B. 修改回應 (Response Modification)
如果你想要在後端處理完畢,準備回傳給使用者瀏覽器時,注入 Cookies 或 Security Headers,你需要處理 Response 物件。
關鍵語法:await nextProxy.next() 會執行請求並回傳 Response 物件,你可以修改這個物件後再 return。
// src/proxy.ts
import { nextProxy } from 'next/server';
export default nextProxy({
async handle(request) {
// 1. 執行請求,並等待後端回應
const response = await nextProxy.next();
// 2. 修改 Response Headers (例如加入安全標頭)
response.headers.set('X-Frame-Options', 'DENY');
// 3. 設定 Cookies (注意:這是寫在 Response 上)
response.cookies.set('visited', 'true', { path: '/' });
// 4. 回傳最終回應給瀏覽器
return response;
},
});
C. 轉址與重寫 (Redirect & Rewrite)
- 轉址 (Redirect):瀏覽器網址列 會改變。
return Response.redirect(new URL('/login', request.url)); - 重寫 (Rewrite):瀏覽器網址列 不變,內容被替換。
return nextProxy.rewrite(new URL('/proxy/target', request.url));
D. 完整範例:身份驗證與標頭注入
結合上述觀念,一個常見的生產環境 Proxy 如下:
// src/proxy.ts
import { nextProxy } from 'next/server';
export default nextProxy({
async handle(request) {
const { pathname } = request.nextUrl;
// 1. 檢查權限 (Redirect)
if (pathname.startsWith('/dashboard')) {
const token = request.cookies.get('auth_token');
if (!token) {
return Response.redirect(new URL('/login', request.url));
}
}
// 2. 注入 Request Headers (給後端看)
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-request-start', Date.now().toString());
// 3. 取得 Response (可以帶入修改過的 Request)
const response = await nextProxy.next({
request: { headers: requestHeaders },
});
// 4. 注入 Response Headers (給前端看)
response.headers.set('x-powered-by', 'Next.js Proxy');
return response;
},
});
從 middleware.ts 遷移
如果你的專案還在用舊的 middleware.ts,Next.js 提供了自動化工具協助遷移:
npx @next/codemod@canary middleware-to-proxy
這個指令會自動:
- 將
middleware.ts重新命名為proxy.ts。 - 將
export function middleware轉換為export default nextProxy結構。 - 更新相關的型別定義。
常見問題
Q: Proxy 中可以使用資料庫嗎?
不建議。proxy.ts 仍然運行在 Edge Runtime,這意味著它沒有完整的 Node.js 環境,且任何延遲都會直接阻斷使用者的請求 (Blocking)。複雜的邏輯請留在 Route Handlers (route.ts) 或 Server Actions 中處理。
Q: Matcher 的規則有變嗎?
沒有變。config.matcher 的語法與舊版 Middleware 完全相容,你依然可以使用 Regex 來排除靜態資源檔案。