Penggunaan NEXT_PUBLIC_ di Environment Production: Hal-Hal yang Perlu Anda Ketahui
Published on
Melalui artikel ini, kami ingin membagikan pengalaman selama mengembangkan aplikasi berbasis Next.js. Beragam proyek yang telah kami selesaikan dapat Anda lihat melalui halaman portofolio kami. Dari berbagai proyek tersebut, ada satu hal yang terlihat simple, tetapi ternyata dapat memberikan dampak besar terhadap proses deployment, yaitu penggunaan NEXT_PUBLIC_.
Secara sederhana,
NEXT_PUBLIC_merupakan prefix yang digunakan pada environment variable agar nilainya dapat diakses oleh kode yang berjalan di sisi client (browser). Mekanisme ini memudahkan aplikasi mengakses konfigurasi yang memang perlu diketahui oleh client, seperti URL API atau konfigurasi layanan pihak ketiga.
Dalam proses pengembangan, aplikasi umumnya dibangun di environment development, kemudian melalui tahap build sebelum dideploy ke environment testing maupun production. Pendekatan ini dikenal sebagai “build once, deploy everywhere”, yaitu satu hasil build yang sama dapat digunakan di berbagai environment tanpa perlu melakukan proses build ulang. Meski terdengar sederhana, konsep tersebut berkaitan erat dengan cara Next.js menangani NEXT_PUBLIC_, yang jika tidak dipahami dengan baik dapat menimbulkan masalah ketika aplikasi dijalankan di production.
Sebagai contoh, untuk menentukan alamat API, Anda dapat membuat environment variable API_URL. Nilainya kemudian disesuaikan dengan environment yang digunakan, misalnya API_URL=http://api-dev.website.com pada environment development dan API_URL=https://api-prod.website.com pada environment production. Dengan pendekatan ini, kode aplikasi tidak perlu diubah karena hanya nilai environment variable yang berbeda di setiap environment.
Namun, mekanisme tersebut tidak selalu berjalan mulus pada Next.js, terutama saat menggunakan environment variable NEXT_PUBLIC_. Ketika menjalankan proses npm run build, Next.js akan menyisipkan (embed) nilai dari beberapa environment variable langsung ke dalam hasil build yang tersimpan di folder .next. Akibatnya, nilai tersebut menjadi hardcoded, yang tidak dapat diubah hanya dengan mengganti environment variable setelah proses build selesai.
Kondisi ini membuat setiap environment, seperti development, staging, maupun production, berpotensi memerlukan hasil build yang berbeda. Bagi tim pengembang maupun operasional, pendekatan tersebut tentu kurang ideal karena menambah kompleksitas proses deployment dan bertentangan dengan prinsip build once, deploy everywhere.
Lantas, bagaimana cara tetap menerapkan prinsip tersebut pada aplikasi Next.js? Kami akan membahas beberapa panduan yang dapat digunakan agar satu hasil build tetap dapat dideploy ke berbagai environment tanpa perlu melakukan proses build ulang.
Daftar Isi
Pitfall 1: Credential Bocor karena Menggunakan NEXT_PUBLIC_
Salah satu pitfall yang sering ditemui adalah menyimpan credential atau data sensitif pada environment variable NEXT_PUBLIC_, seperti API key, access token, kata sandi, rahasia autentifikasi, atau informasi rahasia lainnya. Terutama pada aplikasi hasil Vibe Code.
Untuk memahami mengapa hal ini berpotensi menimbulkan risiko, kita perlu mengenal perbedaan antara Client-Side Rendering (CSR) dan Server-Side Rendering (SSR).
Pada CSR, proses rendering berlangsung di browser sehingga kode yang dijalankan di sisi client hanya dapat mengakses environment variable yang diawali dengan NEXT_PUBLIC_. Karena kode tersebut dikirim ke browser, seluruh nilainya juga dapat dilihat oleh pengguna.
Jadi, sebaiknya hindari penggunaan environment variable NEXT_PUBLIC_ untuk menyimpan credential, access token, maupun data sensitif lainnya.
Sebaliknya, pada SSR, proses rendering dilakukan di server Next.js sebelum halaman dikirim ke browser. Karena berjalan di sisi server, aplikasi dapat mengakses environment variable yang bersifat rahasia tanpa mengekspos nilainya kepada pengguna. Inilah alasan mengapa credential dan konfigurasi sensitif sebaiknya hanya digunakan pada kode yang berjalan di server, bukan pada environment variable NEXT_PUBLIC_.

Untuk mengatasi masalah kebocoran yang bersifat credential, salah satu pendekatan yang umum digunakan adalah proxy rewrites. Melalui mekanisme ini, akses ke Backend API dialihkan melalui server Next.js sehingga frontend hanya menerima data yang dibutuhkan, sementara credential seperti API key atau access token tetap aman di sisi server dan tidak pernah dikirim ke browser.
Implementasi proxy rewrites sedikit berbeda antara Pages Router dan App Router. Pada Pages Router, misalnya, Anda dapat membuat proxy menggunakan catch-all API Route dengan konsep seperti berikut.
// pages/api/[...path].js
import httpProxyMiddleware from "next-http-proxy-middleware";
// Jangan parse body pada route proxy
export const config = { api: { bodyParser: false } };
export default (req, res) => {
// process.env dibaca di dalam route = runtime, bukan build time
const TARGET = process.env.BACKEND_BASE_URL;
if (!TARGET) {
return res.status(500).json({ message: "BACKEND_BASE_URL is not set" });
}
return httpProxyMiddleware(req, res, {
target: TARGET,
changeOrigin: true,
pathRewrite: [{ patternStr: "^/api", replaceStr: "" }],
});
};
Dalam implementasinya, proxy dapat dikembangkan untuk berbagai kebutuhan, seperti mengarahkan robots.txt dan sitemap ke backend CMS, memproxy media atau CDN, hingga meneruskan header seperti Host dan X-Forwarded-*.
Berikut contoh prompt yang dapat digunakan untuk menyesuaikan implementasinya sesuai kebutuhan:
Prompt:
Buatkan catch-all proxy API route Next.js Pages Router di pages/api/[...path].js menggunakan next-http-proxy-middleware.
Tujuannya:
- Semua target proxy dibaca dari
process.envdi dalam handler (runtime, bukan build time) agar satu build bisa dipakai di semua environment. - Route dalam daftar exclude (misalnya
/route-exclude-1,/route-exclude-2,/route-exclude-3) tidak diproxy dan return 404; sisanya diproxy ke target. - Forward header
Host,X-Forwarded-Host,X-Real-IP,X-Forwarded-For,X-Forwarded-Protoagar backend mengira request datang dari domain frontend.
Direkomendasikan:
- Set
export const config = { api: { bodyParser: false } }. - Validasi env variable dan return 500 dengan pesan jelas jika kosong.
- Bungkus proxy dalam Promise dan tangani error tanpa double-send response.
Perlu Dihindari:
- Jangan baca
process.envdi luar handler / di module scope. - Jangan gunakan
NEXT_PUBLIC_*sama sekali. - Jangan hardcode URL atau domain apapun di dalam kode.
Pitfall 2: Pertimbangkan Penggunaan NEXT_PUBLIC_ di Production
Di Tonjoo, kami memilih untuk tidak menggunakan NEXT_PUBLIC_ pada aplikasi production. Bukan karena fitur ini kurang baik, melainkan karena perilakunya yang bergantung pada proses build memerlukan perhatian lebih dalam proses deployment. Dalam skenario yang kami hadapi, hal tersebut kurang sesuai dengan kebutuhan operasional yang mengutamakan fleksibilitas konfigurasi di setiap environment.
Untuk konfigurasi frontend yang tidak bersifat sensitif, seperti feature flag maupun penyesuaian tag tertentu, kami tetap memilih pendekatan lain dibandingkan NEXT_PUBLIC_. Dengan begitu, konfigurasi dapat diubah pada saat runtime tanpa perlu melakukan proses build ulang.
Sebagai alternatif, kami menerapkan pola Configurator Pattern:
- Buat api
/api/configmenggunakan SSR - CSR yang membutuhkan variabel bisa mendapatkan value dari
/api/config
// pages/api/config.js
// SSR — process.env dibaca ketika runtime, bukan ketika build
export default function handler(req, res) {
res.status(200).json({
apiUrl: process.env.API_URL,
featureFlagX: process.env.FEATURE_FLAG_X === "true",
});
}
// Di sisi client, cukup fetch /api/config
const [config, setConfig] = useState(null);
useEffect(() => {
fetch("/api/config").then((r) => r.json()).then(setConfig);
}, []);
Untuk implementasi lengkap (custom hook useConfig, loading state, versi App Router dengan force-dynamic), Anda dapat menggunakan prompt berikut:
Prompt:
Implementasikan configurator pattern pada project Next.js saya agar client component bisa membaca environment variable saat runtime tanpa NEXT_PUBLIC_*.
Tujuan:
- Endpoint
/api/configyang membacaprocess.envdi dalam handler dan mengembalikan JSON berisiapiUrl,cdnUrl, danfeatureFlagX(boolean). - Custom hook
useConfig()dihooks/useConfig.jsyang fetch/api/configsekali, return{ config, loading }. - Satu contoh page yang memakai hook tersebut, dengan loading state.
- Sediakan dua versi: Pages Router dan App Router (untuk App Router tambahkan
export const dynamic = "force-dynamic"pada route handler).
Direkomendasikan:
- Baca
process.envhanya di dalam route handler. - Expose hanya variabel yang memang aman dilihat browser.
Perlu Dihindari:
- Jangan gunakan
NEXT_PUBLIC_*. - Jangan masukkan secret/credential ke response
/api/config— endpoint ini bisa dibaca publik. - Jangan baca
process.envdi module scope atau di client component.
Metode ini bisa bekerja karena /api/config merupakan API Route (SSR), sehingga process.env dibaca setiap kali request diterima oleh server, bukan saat proses npm run build. Hasilnya, perubahan environment variable pada server production dapat diterapkan tanpa perlu melakukan build ulang. Setelah file .env diperbarui, cukup di-restart agar konfigurasi terbaru dapat digunakan.
Pitfall 3: Hal yang Perlu Diperhatikan Saat Mengakses Environment Variable
Selain NEXT_PUBLIC_, penggunaan environment variable lain di Next.js juga perlu diperhatikan. Terutama process.env yang diakses melalui Route Handler agar nilainya dapat dibaca oleh Next.js pada saat runtime. Dengan pendekatan ini, konfigurasi dapat mengikuti environment yang digunakan tanpa bergantung pada hasil npm run build.
// ❌ Di luar handler: nilai berisiko disisipkan (baked) ketika npm run build
const API_SECRET = process.env.API_SECRET;
export default function handler(req, res) {
// ✅ Di dalam handler: nilai diambil dari runtime environment
res.status(200).json({
apiSecret: process.env.API_SECRET,
nodeEnv: process.env.NODE_ENV,
});
}
Pemanggilan process.env di luar route akan menyebabkan nilai akan di-hardcode ketika npm run build!
Pengujian!
Setelah seluruh konfigurasi selesai diterapkan, langkah berikutnya adalah melakukan pengujian. Jalankan npm run build, kemudian ubah nilai environment variable dan pastikan aplikasi menggunakan konfigurasi terbaru sesuai environment yang digunakan. Jika perubahan berhasil diterapkan tanpa perlu melakukan build ulang, berarti implementasi telah berjalan sesuai harapan.
Salah satu cara untuk memverifikasi implementasi adalah menjalankan npm run build tanpa menggunakan file .env. Anda dapat menonaktifkan sementara seluruh isi file .env, misalnya:
API_URL = xxx
Proses build seharusnya tetap berhasil dijalankan. Setelah itu, kembalikan konfigurasi pada file .env, jalankan npm run start, lalu pastikan aplikasi menggunakan nilai environment variable yang telah diperbarui.
Catatan: Pembahasan pada artikel ini berfokus pada skenario di mana proses
npm run buildtidak memerlukan akses ke database. Jika aplikasi Anda membutuhkan koneksi database saat proses build, implementasinya memiliki pertimbangan yang berbeda dan akan dibahas pada artikel terpisah.
Environment variable sering kali dianggap sebagai bagian kecil dari proses pengembangan. Namun, berdasarkan pengalaman kami dalam mengembangkan aplikasi Next.js, pengelolaannya dapat memberikan dampak yang signifikan terhadap kelancaran proses deployment di lingkungan production.
Siap Mengembangkan Aplikasi Next.js yang Mudah Dikelola?
Berdasarkan pengalaman kami mengembangkan aplikasi Next.js selama beberapa tahun, salah satu tantangan yang paling sering ditemui adalah membangun aplikasi yang dapat berjalan dengan konsisten di berbagai environment melalui pipeline CI/CD. Pengalaman tersebut mendorong kami untuk lebih memahami bagaimana Next.js mengelola environment variable, terutama dalam kaitannya dengan proses build dan deployment.
Semoga pengalaman yang kami bagikan dalam artikel ini dapat menjadi referensi bagi Anda yang ingin menerapkan prinsip build once, deploy everywhere pada aplikasi Next.js. Jika Anda membutuhkan partner untuk mengembangkan aplikasi Next.js yang sesuai dengan kebutuhan bisnis, tim Tonjoo siap membantu mulai dari tahap perencanaan hingga implementasi di lingkungan production.
Updated on July 24, 2026 by Anisa K.