Middleware Next.js bisa menjadi garis pertahanan pertama yang efektif untuk otentikasi, validasi sesi, pengelolaan secret, serta deteksi penyalahgunaan sebelum request mencapai API Route. Dengan menempatkan logika ini di edge runtime, Anda menegakkan aturan keamanan secara global sekaligus menjaga latensi tetap rendah.

Artikel ini menjelaskan implementasi middleware yang mengecek header Bearer token, menyimpan secret secara aman, menerapkan rate limit berbasis IP, serta mencatat audit sederhana. Penjelasan juga mencakup teknik caching token dan pertimbangan deployment di Vercel/edge platform untuk mendukung performa sekaligus keamanan.

1. Struktur middleware Next.js untuk validasi Auth dan Secret

Mulailah dengan middleware di middleware.ts untuk menangkap request sebelum mencapai route. Gunakan NextRequest dari next/server agar bisa berjalan di edge runtime. Pada level ini, pindai header Authorization, cocokan dengan secret (JWT, API key, dsb), lalu kembalikan NextResponse yang sesuai.

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

const SECRET_KEYS = new Set([process.env.API_SECRET_KEY]);

export async function middleware(req: NextRequest) {
  const auth = req.headers.get('authorization')?.split(' ');
  if (auth?.[0] !== 'Bearer' || !auth[1]) {
    return NextResponse.json({ message: 'Unauthorized' }, { status: 401 });
  }

  const token = auth[1];
  if (!isValidToken(token)) {
    return NextResponse.json({ message: 'Invalid token' }, { status: 403 });
  }

  req.headers.set('x-user-id', extractUserId(token));
  return NextResponse.next();
}

function isValidToken(token: string) {
  return SECRET_KEYS.has(token) || verifyJwt(token);
}

Alihkan pemanggilan verifyJwt ke utilitas yang memvalidasi signature dan expiry token. Semua secret disimpan melalui variabel lingkungan (pastikan terenkripsi dan tidak dicheck-in). Hindari logging raw secrets.

1.1 Strategi caching token

Verifikasi JWT atau API key penuh bisa mahal bila dilakukan ulang. Gunakan token cache berbasis Map simpel di edge middleware dengan TTL rendah untuk mencegah berjalannya verifikasi kriptografi pada tiap request.

const tokenCache = new Map();

function verifyJwt(token: string) {
  const cached = tokenCache.get(token);
  if (cached && cached.expires > Date.now()) {
    return cached.valid;
  }

  const valid = jwtVerify(token, secretKey);
  tokenCache.set(token, { expires: Date.now() + 5000, valid });
  return valid;
}

TTL pendek (misalnya 5 detik) mencegah cache menjadi vektor replay saat token dicuri. Pastikan cache diinisialisasi hanya di scope edge worker (tidak di dalam handler asynchronous yang bisa meng-reset).

2. Rate limit dan deteksi abuse

Jaga endpoint publik dengan rate limit sederhana berbasis IP. Karena middleware berjalan di edge runtime, gunakan penyimpanan external seperti Upstash Redis atau in-memory store untuk window counter.

const RATE_LIMIT = 60;
const WINDOW_MS = 60 * 1000;

export async function rateLimit(req: NextRequest) {
  const ip = req.ip ?? 'unknown';
  const key = `rl:${ip}`;
  const { current } = await redisClient.multi()
    .incr(key)
    .px(key, WINDOW_MS)
    .exec();

  if (current > RATE_LIMIT) {
    return NextResponse.json({ message: 'Too many requests' }, { status: 429 });
  }

  return null;
}

Dirangkai dalam middleware utama, panggil rateLimit sebelum memvalidasi token agar request berlebih ditolak lebih cepat. Ingat bahwa ketika menggunakan edge cache seperti Vercel Edge Config, Anda bisa menyimpan counter state ringan tanpa harus mengandalkan Redis jika volume rendah.

2.1 Deteksi abuse tambahan

Tambahkan log audit sederhana setiap kali rate limit tercapai atau token gagal validasi:

function logAudit(req: NextRequest, detail: string) {
  const payload = {
    path: req.nextUrl.pathname,
    ip: req.ip,
    detail,
    timestamp: new Date().toISOString(),
  };
  console.log('audit:', JSON.stringify(payload));
}

Log ini bisa diteruskan ke layanan observability (misalnya Vercel Logs, Datadog) dari edge worker. Pastikan payload tidak menyertakan token atau secret secara langsung.

3. Menjaga secret dan sesi tetap aman

Selain caching token, hindari menyimpan secret aplikasi dalam bundel. Gunakan Edge Environment Variables di Vercel atau Secrets Manager lain. Di middleware, cukup referensi nama variabel.

Untuk sesi, gunakan cookie signed yang hanya dikirim saat HTTPS. Middleware bisa membaca cookie dan memverifikasi signature sebelum meneruskan request:

const SESSION_COOKIE = 'app_session';

function validateSession(req: NextRequest) {
  const cookie = req.cookies.get(SESSION_COOKIE)?.value;
  if (!cookie) return false;
  return verifyJwt(cookie);
}

Jika perlu memblokir request dari sesi kadaluarsa, hapus cookie melalui NextResponse.rewrite atau NextResponse.redirect setelah menyebabkan logout.

4. Catatan deployment di Vercel/Edge Platform

Ketika Anda deploy ke Vercel, middleware berjalan di edge runtime secara otomatis. Berikut poin penting:

  • Size limit: Kurangi dependensi berat. Gunakan utilitas ringan (misalnya jose untuk JWT) karena bundle middleware harus cepat di-download di edge.
  • Cold start: Caching token sebaiknya tidak bergantung pada instance lokal yang sering restart. Pastikan tidak menyimpan state kritikal pada proses karena Vercel dapat menskalakan ke instans baru.
  • Rate limit global: Gunakan Redis/edge database yang mendukung multi-region agar batas diterapkan konsisten.
  • Secret leakage: Jangan log header Authorization; hanya catat status kode dan IP jika perlu.

Jika menggunakan platform lain (Cloudflare Workers, Fastly), prinsipnya sama: tempatkan middleware di jalur request awal, hindari state lokal yang tidak dapat dibagikan, dan pastikan integrasi observability berjalan.

Kesimpulan

Menguatkan middleware Next.js berarti menggabungkan validasi auth, caching token, audit, dan rate limit dalam satu lapisan edge. Fokus pada pengelolaan secret yang aman dan penggunaan storage eksternal untuk state yang dinamis. Dengan pendekatan ini, API Anda tetap responsif di edge sekaligus tangguh terhadap penyalahgunaan.