gotchatypescriptnextjsMajor
Environment variables in Next.js: server vs client exposure rules
Viewed 0 times
Next.js 9.4+ for .env.local support
NEXT_PUBLIC_environment variablesserver envclient envbuild-time.env.local
Problem
Environment variables set in .env.local are undefined in the browser, or sensitive server variables are accidentally exposed to clients by prefixing them with NEXT_PUBLIC_.
Solution
Follow Next.js env var exposure rules:
// .env.local
// SERVER ONLY (never in client bundle):
DATABASE_URL=postgresql://...
STRIPE_SECRET_KEY=sk_live_...
// CLIENT SAFE (intentionally in client bundle):
NEXT_PUBLIC_STRIPE_KEY=pk_live_...
NEXT_PUBLIC_API_URL=https://api.example.com
// Server Component — can read any env var:
export default async function Page() {
const db = new PrismaClient({
datasourceUrl: process.env.DATABASE_URL, // server only
});
}
// Client Component — only NEXT_PUBLIC_ vars are available:
'use client';
export function PaymentButton() {
const key = process.env.NEXT_PUBLIC_STRIPE_KEY; // safe
const secret = process.env.STRIPE_SECRET_KEY; // undefined in browser!
}
// .env file precedence (highest to lowest):
// .env.local > .env.development.local > .env.development > .env
// .env.local
// SERVER ONLY (never in client bundle):
DATABASE_URL=postgresql://...
STRIPE_SECRET_KEY=sk_live_...
// CLIENT SAFE (intentionally in client bundle):
NEXT_PUBLIC_STRIPE_KEY=pk_live_...
NEXT_PUBLIC_API_URL=https://api.example.com
// Server Component — can read any env var:
export default async function Page() {
const db = new PrismaClient({
datasourceUrl: process.env.DATABASE_URL, // server only
});
}
// Client Component — only NEXT_PUBLIC_ vars are available:
'use client';
export function PaymentButton() {
const key = process.env.NEXT_PUBLIC_STRIPE_KEY; // safe
const secret = process.env.STRIPE_SECRET_KEY; // undefined in browser!
}
// .env file precedence (highest to lowest):
// .env.local > .env.development.local > .env.development > .env
Why
Next.js inlines NEXT_PUBLIC_ variables into the JavaScript bundle at build time using static string replacement. Server-only variables remain in the Node.js process and are never sent to the browser. This is why NEXT_PUBLIC_ changes require a rebuild to take effect.
Gotchas
- NEXT_PUBLIC_ variables are baked into the build — changing them in production requires a rebuild
- process.env.NEXT_PUBLIC_X evaluates to a literal string at build time — dynamic property access won't work for public vars
- Vercel and other platforms automatically set env vars from their dashboard — no .env.local needed in production
- .env.local is gitignored by default — use .env.example to document required vars
Code Snippets
Next.js environment variable exposure rules
// Rule:
// process.env.DATABASE_URL → server only (Server Components, Route Handlers)
// process.env.NEXT_PUBLIC_URL → available everywhere, baked in at build timeContext
When configuring environment variables for a Next.js application across server and client
Revisions (0)
No revisions yet.