← Back to Home
FRAMEWORK · SECURITY

Next.js: API Key yang Bocor ke Browser dan Cara Menutupnya

4 November 2026 · 5 min read · AISG Editorial

Aplikasi Next.js yang dibangun cepat, apalagi dengan bantuan AI, sering memanggil layanan pihak ketiga langsung dari komponen di browser. Kodenya berjalan, fiturnya muncul, dan tidak ada error. Masalahnya baru terlihat kalau seseorang membuka DevTools: kunci API ikut terkirim ke setiap pengunjung, tertanam di berkas JavaScript yang bisa diunduh siapa saja.

Artikel ini menjelaskan kapan nilai rahasia di Next.js berpindah ke browser, cara membuktikannya sendiri dalam sepuluh menit, pola yang benar untuk memanggil API berbayar, dan apa yang harus dilakukan bila kunci sudah terlanjur terekspos.

Aturan dasar: variabel server vs NEXT_PUBLIC_

Next.js membedakan dua jenis variabel lingkungan. Perbedaan ini menentukan apakah sebuah nilai tinggal di server atau ikut dikirim ke browser.

Jenis variabelDi mana bisa dibacaIkut ke bundle browser?
OPENAI_API_KEY (tanpa awalan)Kode server: Route Handler, Server Component, Server Action, API RouteTidak
NEXT_PUBLIC_OPENAI_API_KEYServer dan browserYa — nilainya ditulis langsung ke JavaScript saat build
Properti env di next.config.jsServer dan browserYa — juga disisipkan saat build

Kata kuncinya: disisipkan saat build. Variabel berawalan NEXT_PUBLIC_ bukan dibaca saat halaman dibuka; nilainya sudah menjadi teks biasa di dalam berkas /_next/static/chunks/*.js. Menghapus variabel itu dari dashboard hosting tanpa build ulang tidak mengubah apa pun pada berkas yang sudah terbit.

Lima jalur kunci API bocor ke browser

  1. Awalan NEXT_PUBLIC_ pada kunci rahasia. Paling umum. Sering terjadi karena pesan error "variable is undefined" di komponen client, lalu solusinya menambah awalan supaya error hilang.
  2. Memanggil API berbayar langsung dari komponen "use client". Untuk memanggil OpenAI, Stripe, atau layanan email dari browser, kuncinya harus ada di browser. Tidak ada cara aman untuk itu.
  3. Properti env di next.config.js. Nilai di sini disisipkan ke bundle seperti NEXT_PUBLIC_, walau namanya tidak berawalan apa pun.
  4. Modul server ikut terimpor ke komponen client. Berkas utilitas yang membaca kunci dari process.env lalu diimpor oleh komponen client bisa menyeret konfigurasi yang tidak seharusnya ke bundle.
  5. Source map produksi. Bila productionBrowserSourceMaps dinyalakan, berkas .map memperlihatkan kode sumber asli, termasuk konstanta yang ditulis langsung di kode.

Kunci mana yang memang boleh ada di browser

Tidak semua kunci di browser adalah kebocoran. Beberapa layanan memang dirancang memakai kunci publik, asalkan pengamannya ada di tempat lain.

Boleh di browser (dengan syarat)Tidak boleh di browser
Stripe publishable key pk_live_…Stripe secret key sk_live_…
Supabase anon key — hanya bila Row Level Security aktif dan benarSupabase service_role key (melewati semua RLS)
Konfigurasi web Firebase (apiKey) — keamanan ada di Security RulesKunci OpenAI / Anthropic sk-…
Google Maps key yang dibatasi HTTP referrerAWS access key, token GitHub, URL database berisi password

Untuk kolom kiri, kunci yang terlihat bukan masalah, tetapi aturan di belakangnya wajib diperiksa. Lihat apakah Supabase aman untuk produksi dan Firebase rules allow all.

Cara membuktikan sendiri dalam 10 menit

1. DevTools: cari di semua berkas yang dimuat

Buka situs produksi, tekan F12, buka tab Sources, lalu tekan Ctrl+Shift+F (Mac: Cmd+Option+F) untuk mencari di semua berkas. Cari pola berikut satu per satu: sk-, sk_live_, AKIA, service_role, ghp_. Buka juga beberapa halaman lain di situs Anda, karena Next.js memuat potongan JavaScript per halaman: kunci bisa saja hanya ada di halaman dashboard atau checkout.

2. Tab Network: siapa yang memanggil API berbayar

Di tab Network, pakai fitur aplikasi yang memanggil AI atau pembayaran. Jika ada permintaan langsung dari browser ke api.openai.com, api.anthropic.com, atau api.stripe.com dengan header Authorization, kuncinya pasti ada di browser. Yang benar: browser memanggil endpoint di domain Anda sendiri, lalu server Anda yang meneruskan.

3. Folder hasil build

Di mesin pengembangan, jalankan build produksi lalu cari di berkas statis yang akan dikirim ke browser:

npm run build
grep -rEo "sk-[A-Za-z0-9_-]{20,}|sk_live_[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16}" .next/static | head

Hasil kosong adalah tanda baik untuk pola itu, tetapi bukan jaminan untuk semua jenis kunci. Tambahkan pola layanan lain yang Anda pakai.

4. Daftar variabel yang berawalan NEXT_PUBLIC_

grep -rn "NEXT_PUBLIC_" --include=*.ts --include=*.tsx --include=*.js . | grep -v node_modules

Periksa setiap nama yang muncul: apakah nilainya memang publik menurut tabel di atas?

Pola yang benar: Route Handler sebagai perantara

Pindahkan pemanggilan API berbayar ke server. Kunci tetap di variabel tanpa awalan, dan browser hanya berbicara dengan endpoint milik Anda.

// app/api/chat/route.ts — berjalan di server
import "server-only";

export async function POST(req: Request) {
  const { message } = await req.json();
  // batasi input + wajibkan sesi login sebelum memakai kuota berbayar
  const res = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: String(message).slice(0, 2000) }],
    }),
  });
  return new Response(res.body, { status: res.status });
}

Tiga hal yang sering terlupa setelah memindahkan kunci:

Sudah terlanjur bocor? Urutannya penting

  1. Rotasi kunci lebih dulu di dashboard penyedia layanan, lalu pasang kunci baru di variabel server. Memperbaiki kode tanpa rotasi tidak cukup: kunci lama sudah terkirim ke setiap pengunjung sebelumnya.
  2. Periksa deployment lama. Beberapa platform hosting menyimpan setiap deployment dengan URL tersendiri, termasuk preview. Berkas lama yang masih memuat kunci tetap bisa diakses sampai deployment itu dihapus atau dilindungi.
  3. Periksa pemakaian. Lihat log tagihan dan pemakaian di penyedia layanan sejak tanggal kunci pertama kali terbit.
  4. Build ulang dan cek lagi dengan langkah DevTools di atas.

Langkah rotasi per penyedia dibahas di cara rotasi API key yang bocor.

Cek otomatis tanpa install

Free URL scan AISG memeriksa HTML halaman yang Anda masukkan, hingga tiga berkas JavaScript pertama yang dimuat dari domain yang sama, dan source map-nya, untuk pola kunci umum seperti OpenAI, Anthropic, Stripe secret, AWS, GitHub, Slack, dan Google. Nilai yang ditemukan selalu disamarkan di laporan. Batasnya perlu diketahui: potongan JavaScript yang baru dimuat di halaman lain tidak ikut diperiksa, jadi pencarian manual di DevTools tetap perlu untuk halaman dashboard dan checkout. Hasil kosong berarti pola itu tidak terdeteksi di berkas tersebut, bukan jaminan aman.

Ringkasnya: kunci rahasia di Next.js aman selama ia tidak pernah diberi awalan NEXT_PUBLIC_, tidak dipanggil dari komponen client, dan endpoint perantaranya dilindungi login serta batas laju.

Bacaan lanjutan: API key bocor di aplikasi vibe coding: cara cek · cara rotasi API key yang bocor. Mau memeriksa aplikasi sendiri? free URL scan — tanpa akun.
Next.js API Key Frontend Vibe Coding
-->
Tag1 Tag2 Tag3

Start with the first step.

Start free on Community — local scan, no credit card.

Start Scanning
Early access: AISG is currently in early-access mode — the Community plan is free to start. Scans must be performed only against targets you are authorized to assess — results are indicative and require human review (see Trust & Legal).