Middleware Next.js berjalan di edge sebelum route handler atau Server Component apa pun. Ini adalah salah satu fitur paling powerful dan paling disalahpahami dari framework ini. Situs portofolio saya (matthewswong.com) menggunakan middleware untuk deteksi locale i18n dengan next-intl. Aplikasi ERP yang saya bangun menggunakan middleware untuk redirect autentikasi dan header CSP.
Middleware adalah fungsi yang berjalan di Edge Runtime sebelum request mencapai route handler, Server Component, atau file statis Anda. Ia bisa membaca request, memodifikasi header, menulis ulang URL, redirect, atau mengembalikan respons secara langsung. Yang BUKAN: middleware bukan lapisan otorisasi yang aman. Kerentanan CVE-2025-29927 mendemonstrasikan bahwa middleware bisa dilewati dengan mengirimkan header x-middleware-subrequest yang dibuat. Otorisasi harus terjadi di dalam route handler dan Server Components Anda.
Kasus penggunaan middleware paling umum di Next.js adalah internasionalisasi. Middleware next-intl mendeteksi locale pilihan pengguna dari: (1) awalan URL (/en/, /id/), (2) cookie locale yang ditetapkan oleh kunjungan sebelumnya, (3) header Accept-Language request. Kemudian ia redirect atau menulis ulang request ke jalur locale yang sesuai. Situs portofolio saya menggunakan pola ini — mengunjungi matthewswong.com dengan locale browser Indonesia redirect ke /id secara otomatis.
Request lifecycle with Next.js middleware:
Incoming Request
│
▼
middleware.ts (Edge Runtime)
│
├── 1. i18n: detect locale from URL/cookie/Accept-Language
│ → rewrite /products → /en/products (or /id/products)
│
├── 2. Auth: check session cookie / JWT
│ → if missing: redirect to /[locale]/login
│ → if present: continue (actual authz in route handler)
│
├── 3. Security headers: add CSP, HSTS, X-Frame-Options
│
└── 4. Pass to route handler / Server Component
│
▼
Route Handler (actual authorization lives here)
Server Component (data fetching, rendering)
Middleware matcher — CRITICAL for performance:
export const config = {
matcher: [
'/((?!_next/static|_next/image|images|favicon.ico|api/health).*)',
],
}
// Without this: middleware runs on EVERY request including static filesDari mengintegrasikan middleware next-intl dengan autentikasi Clerk pada route admin matthewswong.com: susun fungsi middleware menggunakan createMiddleware dari next-intl dan bungkus dengan authMiddleware Clerk. Jalankan deteksi i18n terlebih dahulu, lalu pemeriksaan auth, sehingga redirect pergi ke halaman login locale yang benar (mis. /id/login daripada /en/login). Urutan penting — middleware auth perlu mengetahui locale untuk membangun URL redirect yang benar.
Middleware cocok untuk redirect autentikasi — memeriksa apakah cookie sesi ada dan redirect ke /login jika tidak. Yang TIDAK cocok: memeriksa izin, peran, atau akses tingkat resource. Alasannya: middleware berjalan di Edge Runtime, yang memiliki akses terbatas ke database atau logika bisnis kompleks Anda. Periksa validitas token sesi di middleware (verifikasi JWT cepat dan berbasis CPU), redirect pengguna yang tidak terautentikasi, tapi tinggalkan otorisasi aktual ke Server Component atau Route Handler.
// middleware.ts — production example (i18n + auth + CSP headers)
import { NextRequest, NextResponse } from 'next/server'
import createMiddleware from 'next-intl/middleware'
import { routing } from './i18n/routing'
const intlMiddleware = createMiddleware(routing)
const PROTECTED_PATHS = ['/dashboard', '/admin', '/settings']
export function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname
// Run i18n middleware first (handles locale detection + redirect)
const intlResponse = intlMiddleware(req)
// Add security headers to every response
const response = intlResponse ?? NextResponse.next()
response.headers.set('X-Frame-Options', 'DENY')
response.headers.set('X-Content-Type-Options', 'nosniff')
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'nonce-{NONCE}'; style-src 'self' 'unsafe-inline'"
)
// Auth check for protected routes (JWT from cookie)
const isProtected = PROTECTED_PATHS.some(p => pathname.includes(p))
if (isProtected) {
const token = req.cookies.get('auth-token')?.value
if (!token) {
// Extract locale from pathname for correct redirect
const locale = pathname.startsWith('/id') ? 'id' : 'en'
return NextResponse.redirect(new URL(`/${locale}/login`, req.url))
}
// NOTE: Don't verify JWT here for complex auth — do that in the route handler
// Middleware just checks for presence; route handler does actual verification
}
return response
}
export const config = {
matcher: ['/((?!_next/static|_next/image|images|favicon.ico|api/health).*)'],
}
// A/B testing at the edge
export function middleware(req: NextRequest) {
const bucket = req.cookies.get('ab-bucket')?.value ?? (Math.random() > 0.5 ? 'a' : 'b')
const url = req.nextUrl.clone()
url.pathname = `/landing-${bucket}`
const response = NextResponse.rewrite(url)
response.cookies.set('ab-bucket', bucket, { maxAge: 60 * 60 * 24 * 30 })
return response
}Middleware adalah tempat yang sangat baik untuk menambahkan header respons keamanan secara global — Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy. Menetapkannya di middleware memastikan setiap respons mendapatkan header tersebut. Untuk header CSP dengan nonce (diperlukan untuk script inline), middleware adalah satu-satunya tempat untuk menghasilkan nonce dan melampirkannya ke header dan konteks rendering.
Secara default, middleware Next.js berjalan pada SETIAP request termasuk file statis, gambar, dan route API. Gunakan konfigurasi matcher untuk membatasi jalur mana yang memicu middleware Anda. Middleware yang dikonfigurasi buruk yang berjalan pada setiap request /_next/image menambahkan latensi pada setiap pemuatan gambar. Minimal, kecualikan /_next/static, /_next/image, /images/, dan /favicon.ico dari matcher Anda.
Middleware memungkinkan pengujian A/B sisi server yang sebenarnya tanpa flicker sisi klien. Tugaskan pengguna ke varian (menggunakan cookie atau ID pengguna dari JWT), lalu tulis ulang URL ke halaman yang sesuai: /landing-a atau /landing-b, keduanya dapat diakses di /. Pengguna melihat /campaign di URL tapi mendapatkan halaman varian. Karena ini terjadi di edge sebelum rendering apa pun, tidak ada flash dari varian kontrol sebelum varian uji dimuat.
Di setiap proyek Next.js, middleware.ts saya menangani: (1) Deteksi locale i18n dan redirect via next-intl. (2) Pemeriksaan sesi dan redirect auth untuk route yang dilindungi. (3) Header keamanan (CSP, HSTS, X-Frame-Options). Itu saja. Saya tidak menaruh logika bisnis, panggilan database, atau otorisasi kompleks di middleware. Keterbatasan Edge Runtime membuat middleware kompleks rapuh.
Next.js hanya mendukung satu file middleware.ts di root proyek. Untuk middleware kompleks dengan beberapa kekhawatiran (auth + i18n + CSP), saya merantainya menggunakan pola compose: ekspor fungsi utama yang menjalankan setiap kekhawatiran secara berurutan dan menggabungkan header respons. next-intl menyediakan factory createMiddleware yang menangani i18n dan mengembalikan fungsi middleware yang bisa Anda komposisikan.