Next.js Middleware(Middleware)是基於 Edge Network(邊緣網路)架構設計的請求攔截器,會在請求到達 Next.js 頁面渲染或 API 路由前先行執行。透過 Middleware,開發者可以在邊緣端以極低延遲完成身份驗證、路由重定向 (Redirect)、請求重寫 (Rewrite) 及 Header 標頭修改,是優化網站安全與存取效能的關鍵機制。
⚡ 快速跳轉目錄
Next 中的 Middleware 是什麼?
Middleware(Middleware)是一種在請求 (Request) 到達特定路由或 API 之前執行的函式。在 Next.js 中,Middleware 可用於攔截並修改請求,使開發者能夠靈活地進行身份驗證、請求重寫、日誌記錄等。
與傳統 Express.js 或 Koa.js 的 Middleware 不同,Next.js 的 Middleware 預設運行於 Vercel 的 Edge Runtime(基於標準 Web APIs),運行在 Edge Network(邊緣網路),因此具有極快的響應速度。
此外,Express.js 的 Middleware 通常運行於傳統 Node.js 伺服器(Server)內部,支援完整的 Node.js 原生 API 與 req.body 解析,而 Next.js Middleware 則更偏向於請求到達前的輕量化攔截與修改,適合應用於身份驗證、A/B 測試及 SEO 優化等場景。
Middleware 的運行時機與架構流向
Middleware 在請求剛進入 Next.js 應用之前執行,它通常在瀏覽器發送請求後、Next.js 處理路由與頁面渲染前運行,適用情境包含:
- 用戶從瀏覽器地址列輸入網址並按 Enter
- 點擊網頁上的連結以導向其他頁面
- 透過 JavaScript 發送 AJAX / fetch 請求
- 表單提交後觸發的 HTTP 請求 (Form Submit)
請求處理架構流向
[ 瀏覽器 HTTP 請求 ]
│
▼
[ Edge Network: Middleware (攔截/驗證) ]
│
├── (未授權/需跳轉) ──► [ 307 重定向 / Redirect ]
│
▼ (通過/Rewrite)
[ Next.js 應用層 (Pages / App Router / API Routes) ]
Middleware 的基本使用與 TypeScript 範例
1. 建立 middleware.ts
在 Next.js 應用的 src 或根目錄下建立 middleware.ts,建議採用 TypeScript 搭配 NextRequest 與 NextResponse 型別標註:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 取得 Cookie 中的 Token 進行基本驗證
const token = request.cookies.get('auth_token')?.value;
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
2. 設定 Middleware 作用範圍
如果 Middleware 只需要作用於特定路徑,可在 middleware.ts 中匯出 config 物件來設定匹配的路由。
Next.js 提供 matcher 屬性來定義 Middleware 的作用範圍,支援路徑模式匹配。例如:
export const config = {
matcher: [
'/dashboard/:path*',
'/admin/:path*'
]
};
這代表 Middleware 只會作用於 /dashboard/ 和 /admin/ 相關頁面,而不影響其他公開請求。
Middleware 與 API Routes 的差異
要使用 Middleware 前,必須先了解它與 API Routes 的權責分工,兩者的差異如下表:
| 比較項目 | Middleware | API Routes |
|---|---|---|
| 運行位置 | 邊緣網路 (Edge Network) | 應用伺服器端 (Node.js / Serverless) |
| 適用場景 | 攔截請求、重寫 URL、權限驗證、Header 調整 | 處理商業邏輯、資料庫存取、檔案上傳 |
| 延遲時間 | 極低(靠近使用者的 Edge 節點) | 視伺服器地理位置與效能而定 |
| 影響範圍 | 由 matcher 定義的全域或區域請求 | 僅限特定 API 路徑(如 /api/*) |
舉例來說,假設我們有一個用於管理後台的 /admin 路由:
- Middleware 處理身份驗證:攔截請求並檢查使用者是否持有有效的 JWT,若無則於邊緣端直接重定向至
/login。 - API Routes 處理數據請求:在
/api/admin/users內部連接資料庫(如 PostgreSQL 或 MongoDB)執行查詢並返回 JSON 結果。
這樣的組合能確保應用層級的安全性,同時保持數據存取的靈力與效能。
Middleware 的限制與注意事項
雖然 Middleware 執行效率極高,但因其運行環境為 Edge Runtime,在使用上需特別注意以下機制限制:
1. 不支援直接回傳 HTML 或完整 JSON 響應
Edge Middleware 的核心目標是「路由與標頭過濾」,而非取代後端渲染或 API 回應。Middleware 應透過 NextResponse.next()、NextResponse.redirect() 或 NextResponse.rewrite() 傳遞請求。若直接於 Middleware 內回傳自訂 JSON 物件,會引發型別錯誤。若需要處理 API 邏輯並回傳 JSON,應於 API Routes 中實作。
/*** 錯誤寫法 (Wrong Example) ***/
export function middleware(req: NextRequest) {
// ❌ 嚴禁直接將物件作為 Response 回傳
return { status: 401, message: 'Unauthorized' };
}
/*** 解法程式碼 (Correct Example) ***/
export function middleware(req: NextRequest) {
if (!req.cookies.has('session')) {
// ✅ 正確使用 NextResponse 回傳標準 Response 狀態
return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
}
return NextResponse.next();
}
2. Edge Runtime 對 fetch() 的支援與存取邊界
過往常有「Middleware 不支援 fetch()」的誤解。事實上,Edge Runtime 原生完整支援標準 Web API fetch(),開發者可直接在 Middleware 中發送 HTTP 請求(例如向第三方 Token 驗證服務或 Auth0 / Supabase 發送請求)。
但使用 fetch() 時必須注意以下限制:
- 執行逾時限制:Edge Function 的生命週期極短(通常為 1.5 秒至 30 秒內,依部署平台而定),遠端 API 若回應過慢會導致請求失敗。
- 不支援 Node.js 原生模組:無法使用 Node.js 獨有的原生模組(如
fs、net、child_process),僅能呼叫符合 Web Standard API 的套件。
/*** 在 Middleware 中使用標準 fetch() 驗證 Token ***/
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
const token = request.headers.get('Authorization');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
// ✅ Edge Runtime 原生支援 fetch() API
const authResponse = await fetch('https://api.example.com/v1/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
if (!authResponse.ok) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
3. req.body 無法直接讀取與存取權限表
在 Middleware 中,無法直接讀取或解析 req.body(或 HTTP POST 的 Payload)。這是因為 HTTP 請求體屬於數據串流(Stream),若在邊緣端提前消耗(Consume)該串流,後續到達 API Route 或頁面時將無法再次讀取 Body。
以下整理 Middleware 中常見請求屬性的存取限制表:
| 屬性 | 可存取 | 可修改 | 說明 |
|---|---|---|---|
req.url | ✅ | ❌ | 獲取請求的完整 URL 字串 |
req.nextUrl | ✅ | ❌ | Next.js 解析後的 URL 物件,適用於 Rewrite 與 Redirect |
req.method | ✅ | ❌ | 獲取 HTTP 請求方法(如 GET、POST) |
req.headers | ✅ | ✅ | 可讀取與透過 NextResponse 設定請求與回應標頭 |
req.cookies | ✅ | ✅ | 可讀取與寫入 Cookie(如 req.cookies.get('token')) |
req.ip | ✅ | ❌ | 取得客戶端 IP 地址 |
req.body | ❌ | ❌ | 無法讀取,強行解析將導致串流鎖定或錯誤,需於 API Routes 處理 |
常見問題(Q&A)
Q1:能在 Next.js Middleware 中連接資料庫(如 ORM / PostgreSQL)嗎?
不建議。大部分傳統資料庫 ORM(如舊版 Prisma 或 pg 驅動)依賴 Node.js 原生 TCP Socket 模組,無法直接在 Edge Runtime 執行。若需要存取資料庫,建議使用支援 HTTP / WebSockets 的邊緣資料庫 SDK(如 Supabase、Neon、PlanetScale),或將資料庫邏輯移至 API Routes 中。
Q2:為什麼在 Middleware 設定了 response.headers.set(),後端頁面卻讀取不到?
若要將修改後的 Header 傳遞給後續的頁面或 API 路由,必須將修改寫入 request.headers,並於 NextResponse.next({ request: { headers } }) 中帶入,而非僅修改回傳的 Response Header。
// ✅ 正確帶入修改後的 Request Headers
const requestHeaders = new Headers(request.headers);
requestHeaders.set('x-user-id', '12345');
return NextResponse.next({
request: {
headers: requestHeaders,
},
});
Q3:Middleware 檔案可以放置在哪些目錄?
Next.js 規定 middleware.ts(或 .js)必須放置於專案的根目錄(與 package.json 同級)或 src/ 目錄下。每個 Next.js 專案僅能有一個主 Middleware 進入點檔案。
Q4:如何在開發環境確認 Middleware 是否正確執行?
可以在 Middleware 函式內部使用 console.log() 印出日誌。當請求觸發時,訊息會印出於啟動 next dev 的 Terminal 控制台中(而非瀏覽器的 Console)。此外,亦可透過瀏覽器 DevTools 的 Network 分頁檢查 Request / Response Header 變化進行排查。
結論
Middleware 在 Next.js 中提供了強大的請求攔截能力,結合 Edge Network 網路架構,能有效將授權檢查、語系切換與路由轉向等邏輯前移至邊緣端處理。只要掌握 Edge Runtime 的 API 支援邊界並避免在 Middleware 中處理串流 Body 或過重的同步運算,就能大幅減輕伺服器負載,打造具備高安全性與極速響應的現代 Web 應用。
發表迴響