Environment Variables

Securing secrets and exposing public keys.

Next.js3 min readConcept 61 of 66

The NEXT_PUBLIC Prefix

Next.js seamlessly loads .env, .env.local, .env.development, and .env.production files into Node's process.env.

By default, all environment variables are only available in the Node.js/Edge environments (Server Components, Route Handlers, Middleware).

To expose a variable to the browser (Client Components), you must explicitly prefix it with NEXT_PUBLIC_.

Preventing Catastrophic Leaks

If you accidentally bundle your STRIPE_SECRET_KEY or DATABASE_URL into the client's JavaScript, a malicious user can extract it by opening the browser DevTools.

Next.js enforces security by default. It physically strips non-public variables from the client bundle during compilation, replacing them with undefined.

Implementation

env

# .env.local

STRIPE_SECRET_KEY=sk_test_12345

NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_12345

typescript

// Server Component (app/page.tsx)

export default function Page() {

// This works perfectly on the server.

const secret = process.env.STRIPE_SECRET_KEY;

return <ClientComponent publishableKey={process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} />

}

typescript

// Client Component (components/ClientComponent.tsx)

'use client';

export default function ClientComponent() {

// This will be undefined in the browser!

const secret = process.env.STRIPE_SECRET_KEY;

// This works fine.

const pubKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;

}

Environment Variable Compiler

Watch how the Next.js compiler treats private vs public environment variables during a build.

.env.local
STRIPE_SECRET_KEY=sk_live_123456789// Private (Server Only)
NEXT_PUBLIC_STRIPE_PUB=pk_live_987654321// Public (Client + Server)
Compiling Client Bundle...
PrivateComponent.tsx'use client'
export default function Payment() {
  // Returns undefined in browser!
  const key = process.env.STRIPE_SECRET_KEY
;
  return <Stripe apiKey={key} />;
}
Runtime ErrorStripe initialization failed. Expected string for apiKey, received: undefined
PublicComponent.tsx'use client'
export default function Payment() {
  // Safely bundled to the client
  const key = process.env.NEXT_PUBLIC_STRIPE_PUB
;
  return <Stripe apiKey={key} />;
}
Render SuccessStripe loaded successfully with key: pk_live_987654321

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1What happens if you try to read process.env.DATABASE_URL inside a Client Component ('use client')?

Remember this

  • Always use the NEXT_PUBLIC_ prefix for variables the browser needs (e.g., Supabase anon key, Stripe publishable key).
  • Never use NEXT_PUBLIC_ for database passwords, API secrets, or service account keys.
  • .env.local is for local secrets and should be added to .gitignore.

Done with this concept?

Mark it complete to track your progress. No login needed.