Why Your NEXT_PUBLIC_ Variables Might Be Exposing Secrets
Why Your NEXT_PUBLIC_ Variables Might Be Exposing Secrets
If you're using Next.js, any environment variable prefixed with \NEXT_PUBLIC_\ gets bundled directly into your client-side JavaScript. This is by design, but many developers misunderstand what this means: those values are visible to anyone who views your page source.
How NEXT_PUBLIC_ Works
Next.js has two types of environment variables:
Server-side only (safe):\\\bash
# These stay on the server - never sent to browser
DATABASE_URL="postgres://..."
STRIPE_SECRET_KEY="sk_live_..."
API_SECRET="..."
\\\
\\\bash
# These are bundled into JavaScript - visible to everyone
NEXT_PUBLIC_API_KEY="..."
NEXT_PUBLIC_STRIPE_KEY="..."
NEXT_PUBLIC_ANALYTICS_ID="..."
\\\
At build time, Next.js replaces all references to \process.env.NEXT_PUBLIC_*\ with the actual values. This means the literal string ends up in your JavaScript bundle.
What Gets Exposed
Open DevTools on any Next.js site and search the JavaScript files. You might find:
\\\javascript
// In the bundled JavaScript
const apiKey = "sk-abc123...";
const config = { apiKey: "AKIA..." };
\\\
Common mistakes developers make:
| Variable Name | Risk Level | Problem |
|---|---|---|
| NEXT_PUBLIC_API_KEY | High | Generic name, might be a secret key |
| NEXT_PUBLIC_OPENAI_KEY | Critical | OpenAI keys should never be client-side |
| NEXT_PUBLIC_DATABASE_URL | Critical | Database credentials exposed |
| NEXT_PUBLIC_STRIPE_SECRET | Critical | Stripe secret keys allow full account access |
| NEXT_PUBLIC_SUPABASE_ANON_KEY | Low | Actually designed for client-side use |
| NEXT_PUBLIC_GOOGLE_MAPS_KEY | Low | Meant for browser, but should have restrictions |
How to Check Your Site
Method 1: View Source[hash].js\)sk_\, \sk-\, \AKIA\, \api_key\Use DomainOptic's Security Audit to scan your JavaScript bundles for exposed secrets automatically.
The Fix: Move Secrets Server-Side
Before (insecure):\\\javascript
// pages/api/chat.js or app component
const response = await fetch('https://api.openai.com/v1/chat/completions', {
headers: {
'Authorization': \Bearer \${process.env.NEXT_PUBLIC_OPENAI_KEY}\
Run a security audit