Kenapa ini penting
Beberapa keputusan sebaiknya diambil sebelum halaman mulai dirender: pengunjung yang belum login dan mencoba membuka /admin sebaiknya langsung diarahkan ke /login; pengunjung dari URL lama /artikel/... sebaiknya dialihkan ke /blog/...; atau setiap response perlu ditambah header keamanan. Kalau logika seperti ini ditaruh di setiap halaman, kodenya berulang di mana-mana dan mudah terlewat.
Next.js menyediakan satu "pos jaga" di depan seluruh aplikasi: Proxy. Kalau kamu pernah membaca tutorial Next.js lama, fitur ini dulu bernama Middleware dengan file middleware.js. Mulai Next.js 16, Middleware diganti nama menjadi Proxy dengan file proxy.js. Fungsinya sama, namanya diubah supaya tidak tertukar dengan konsep middleware di Express dan supaya jelas perannya: lapisan jaringan yang berdiri di depan aplikasi. File middleware.js masih dikenali tapi sudah deprecated (tidak dianjurkan, akan dihapus di masa depan).
Analogi: Proxy adalah satpam di gerbang kompleks perumahan. Dia tidak masuk ke tiap rumah dan tidak memeriksa isi lemari. Dia hanya melihat sekilas: "Bawa kartu penghuni? Silakan. Tidak bawa? Ke pos tamu dulu." Pemeriksaan detail tetap dilakukan pemilik rumah (halaman dan Data Access Layer).
Konsep inti
Konvensi file
- Buat satu file
proxy.js(atau.ts) di root proyek, sejajar dengan folderapp/. Kalau proyekmu memakai foldersrc/, taruh disrc/proxy.js. - Export function bernama
proxy(atauexport default). Function ini menerimarequest(berupaNextRequest) dan mengembalikan response. - Hanya boleh ada satu file proxy per proyek. Logika yang banyak bisa dipecah ke modul lain lalu di-import.
Yang bisa dilakukan Proxy
| Aksi | Kode | Efek |
|---|---|---|
| Lanjut normal | NextResponse.next() |
Request diteruskan ke halaman |
| Redirect | NextResponse.redirect(new URL('/login', request.url)) |
Browser diminta pindah URL (URL di address bar berubah) |
| Rewrite | NextResponse.rewrite(new URL('/maintenance', request.url)) |
Konten diambil dari path lain, URL di address bar tetap |
| Ubah header | res.headers.set('x-nama', 'nilai') pada hasil NextResponse.next() |
Menambah header response |
| Jawab langsung | Response.json({...}, { status: 403 }) |
Request berhenti di Proxy |
Proxy juga bisa membaca cookie (request.cookies.get('session')?.value), header, dan path (request.nextUrl.pathname).
matcher: kapan Proxy dijalankan
Tanpa konfigurasi, Proxy berjalan di setiap request, termasuk file statis _next/static, gambar _next/image, dan isi folder public/. Itu boros, dan berbahaya: logika redirect bisa tak sengaja memblokir CSS dan gambar sehingga halaman login tampil berantakan. Karena itu export config.matcher:
export const config = {
matcher: ['/admin/:path*', '/login'], // hanya path ini
}
:path*berarti "nol atau lebih segmen setelahnya", jadi/admin,/admin/new, dan/admin/3/editsemuanya cocok.- Bisa juga regex dengan negative lookahead untuk "semua kecuali":
'/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'. - Nilai
matcherharus konstanta (bukan variabel), karena dianalisis saat build. - Ada juga bentuk objek dengan
has/missinguntuk mencocokkan berdasarkan header atau cookie.
Runtime dan batasan
Di Next.js 16, Proxy berjalan di runtime Node.js secara default, jadi kamu bisa memakai library Node seperti jose. Opsi runtime tidak tersedia di file proxy; menuliskannya justru menyebabkan error. Proxy bukan tempat untuk pengambilan data yang lambat: opsi cache pada fetch tidak berlaku di sini, dan setiap milidetik di Proxy menambah waktu tunggu semua halaman yang dicocokkan.
Proxy bukan pengaman utama
Ini pesan terpenting modul ini, dan dokumentasi resmi menekankannya: Proxy cocok untuk pengecekan optimistis (melihat apakah cookie session ada dan valid, lalu redirect), bukan solusi otorisasi lengkap. Alasannya:
- Server Action dipanggil lewat POST ke route tempat ia dipakai. Kalau
matcherdiubah atau action dipindah ke route lain, perlindungan Proxy bisa hilang diam-diam. - Proxy idealnya tidak query database, jadi ia tidak tahu apakah user sudah diblokir atau perannya berubah.
Jadi pola yang benar: Proxy untuk UX (redirect cepat sebelum render), ditambah cek ulang di Data Access Layer dan di setiap Server Action (Modul 6).
Contoh kode
Proxy di proyek akhir: redirect /admin ke /login bila belum login, dan sebaliknya.
// proxy.js (root proyek, sejajar dengan folder app/)
import { NextResponse } from 'next/server'
import { decrypt } from '@/lib/session'
// Cek OPTIMISTIS: cuma baca cookie, tanpa query DB. Keamanan sebenarnya tetap di DAL.
export async function proxy(request) {
const { pathname } = request.nextUrl
const session = await decrypt(request.cookies.get('session')?.value)
const isAdmin = session?.role === 'admin'
if (pathname.startsWith('/admin') && !isAdmin) {
return NextResponse.redirect(new URL('/login', request.nextUrl))
}
if (pathname === '/login' && isAdmin) {
return NextResponse.redirect(new URL('/admin', request.nextUrl))
}
return NextResponse.next()
}
export const config = {
matcher: ['/admin/:path*', '/login'],
}
curl -s -o /dev/null -w "%{http_code} %{redirect_url}\n" http://localhost:3000/admin
# → 307 http://localhost:3000/login (tanpa cookie session)
Saat build, Next.js menampilkan baris ƒ Proxy (Middleware) di ringkasan route, tanda Proxy-mu terdeteksi.
Contoh kedua: kumpulan tugas umum dalam satu Proxy. Pengalihan URL lama, mode maintenance dengan rewrite, dan header tambahan.
// proxy.js (contoh lain, bukan untuk proyek akhir)
import { NextResponse } from 'next/server'
export function proxy(request) {
const { pathname } = request.nextUrl
// 1. URL lama /artikel/slug → /blog/slug (redirect permanen 308)
if (pathname.startsWith('/artikel/')) {
const url = request.nextUrl.clone()
url.pathname = pathname.replace('/artikel/', '/blog/')
return NextResponse.redirect(url, 308)
}
// 2. Mode maintenance: tampilkan isi /maintenance, URL di browser tetap
if (process.env.MAINTENANCE === '1' && pathname !== '/maintenance') {
return NextResponse.rewrite(new URL('/maintenance', request.url))
}
// 3. Lanjut normal + header tambahan
const response = NextResponse.next()
response.headers.set('x-dibuat-dengan', 'Next.js 16')
return response
}
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'],
}
Kalau proyekmu masih punya middleware.js dari versi lama, Next.js menyediakan codemod resmi yang mengganti nama file dan function-nya:
npx @next/codemod@canary middleware-to-proxy .
# middleware.js → proxy.js, export function middleware() → export function proxy()
Untuk redirect yang statis (tidak bergantung cookie atau logika), dokumentasi menganjurkan redirects() di next.config.mjs dulu, karena lebih sederhana dan tidak menjalankan kode di setiap request:
// next.config.mjs (potongan)
const nextConfig = {
async redirects() {
return [{ source: '/artikel/:slug', destination: '/blog/:slug', permanent: true }]
},
}
Kesalahan umum
- Masih menulis
middleware.jsdi proyek Next.js 16. Masih jalan, tapi deprecated. Pakaiproxy.jsdan functionproxy, atau jalankan codemodmiddleware-to-proxy. - Tidak memasang
matcher. Proxy jadi berjalan untuk CSS, JS, dan gambar; redirect auth bisa membuat halaman login tanpa style. Batasi denganmatcheratau negative lookahead. - Menganggap Proxy cukup untuk keamanan. Kalau action dipindah atau matcher diubah, perlindungan hilang. Selalu cek ulang di DAL dan di setiap Server Action.
- Query database atau API lambat di Proxy. Setiap request yang cocok jadi menunggu. Cukup verifikasi token di cookie (cepat, tanpa jaringan).
- Menaruh
proxy.jsdi dalam folderapp/. File harus di root proyek (atau disrc/bila memakai folder src), sejajar denganapp/.
Latihan
- Mudah, Buat Proxy yang menambahkan header
x-waktu-requestberisi waktu ISO ke semua halaman, kecuali file statis. - Sedang, Buat Proxy yang membaca cookie
lang. Kalau pengunjung membuka/dan cookielangbernilaien, rewrite ke/en(URL di browser tetap/). - Menantang, Buat Proxy yang memblokir path
/api/internal/*dengan response JSON403kecuali request membawa headerx-api-keyyang cocok denganprocess.env.INTERNAL_API_KEY.
Lihat solusi
// 1. proxy.js
import { NextResponse } from 'next/server'
export function proxy() {
const res = NextResponse.next()
res.headers.set('x-waktu-request', new Date().toISOString())
return res
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
// curl -I localhost:3000 → x-waktu-request: 2026-10-24T03:15:00.000Z
// 2. proxy.js
import { NextResponse } from 'next/server'
export function proxy(request) {
if (request.cookies.get('lang')?.value === 'en') {
return NextResponse.rewrite(new URL('/en', request.url))
}
return NextResponse.next()
}
export const config = { matcher: '/' }
// 3. proxy.js
import { NextResponse } from 'next/server'
export function proxy(request) {
const key = request.headers.get('x-api-key')
if (!key || key !== process.env.INTERNAL_API_KEY) {
return Response.json({ error: 'Forbidden' }, { status: 403 })
}
return NextResponse.next()
}
export const config = { matcher: '/api/internal/:path*' }
// curl localhost:3000/api/internal/stats → 403 {"error":"Forbidden"}
Ringkasan
- Next.js 16 mengganti nama Middleware menjadi Proxy: file
proxy.jsdi root proyek, export functionproxy. - Proxy bisa
next(),redirect(),rewrite(), mengubah header, atau menjawab langsung. - Selalu batasi dengan
config.matcheragar tidak berjalan untuk file statis. - Proxy berjalan di runtime Node.js secara default; jangan isi dengan kerja lambat.
- Proxy hanya cek optimistis; otorisasi sebenarnya tetap di DAL dan setiap Server Action.