How to Use Environment Variables in Next.js (Without Leaking Them to the Browser)
Learn how to use .env files in Next.js correctly. Understand NEXT_PUBLIC_, avoid common mistakes, and set variables in Vercel and Cloudflare.
On this page
Introduction
Every Next.js developer hits this wall at some point. You add a .env.local file, reference process.env.MY_API_KEY in your code, and everything works fine on your machine. Then you deploy. Either the variable is missing in production, or worse, it shows up in your browser's network tab for anyone to see.
This guide explains how environment variables actually work in Next.js, what the NEXT_PUBLIC_ prefix does, and how to configure things correctly for Vercel, Cloudflare Workers, and any CI/CD pipeline.
Step-by-Step Guide
What Environment Variables Actually Are
Environment variables are key-value pairs your app reads at runtime. They are essential for three reasons:
- Store secrets (API keys, database URLs) outside your source code and away from Git history
- Run the same codebase differently in development, staging, and production without code changes
- Prevent credentials from being hardcoded into files that get committed and shared
The Four .env Files Next.js Reads
Next.js reads .env files in a specific order. When the same variable exists in multiple files, .env.local always wins.
.env # loaded in all environments
.env.local # loaded in all environments, git-ignored (secrets live here)
.env.development # loaded only during next dev
.env.production # loaded only during next build and next startThe NEXT_PUBLIC_ Prefix: Handle with Care
Next.js runs in two contexts, and environment variables behave differently in each:
- The server (Node.js, API routes, Server Components). Variables are available here by default and never exposed to users.
- The client (the browser). Variables are stripped from the bundle at build time unless you explicitly opt in with the
NEXT_PUBLIC_prefix.
# Safe to expose — use NEXT_PUBLIC_
NEXT_PUBLIC_POSTHOG_KEY=phc_xxx
NEXT_PUBLIC_API_URL=https://api.example.com
# Must stay server-only — no NEXT_PUBLIC_ prefix
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=your-secret-hereSetting Up .env.local for Development
Create a .env.local file at the root of your project, at the same level as package.json. How you access a variable depends on the prefix:
- No prefix: server only. Works in Server Components, API routes, and server actions.
- With
NEXT_PUBLIC_prefix: available everywhere, including client components and the browser.
// .env.local
DATABASE_URL=postgresql://localhost:5432/mydb
API_SECRET_KEY=your-secret-here
NEXT_PUBLIC_SITE_URL=http://localhost:3000
// In a Server Component or API route (safe — server only)
const dbUrl = process.env.DATABASE_URL;
// In a Client Component (only works with NEXT_PUBLIC_)
const siteUrl = process.env.NEXT_PUBLIC_SITE_URL;The .env.example Pattern for Teams
.env.local is git-ignored, so anyone cloning your repo will not know which variables are required. The .env.example pattern solves this:
- Create a
.env.examplefile and commit it to Git - List every required variable with blank or placeholder values
- New team members copy it to
.env.localand fill in the real values
# .env.example — commit this to Git
DATABASE_URL=
API_SECRET_KEY=
TURSO_DATABASE_URL=
TURSO_AUTH_TOKEN=
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_POSTHOG_KEY=Setting Variables in Vercel and Cloudflare
Neither platform reads .env files from your repository. Variables must be added through the dashboard and injected at build time:
- Vercel: open your project >
Settings > Environment Variables> add each variable > select environments (Production, Preview, Development) > save > redeploy - Cloudflare: open your project >
Settings > Environment Variables> add variables under Production or Preview >Save and Deploy - Cloudflare Workers runs at the edge, not in Node.js. Test your variable access after the first deploy.
Common Mistakes That Break Things
These four mistakes cause most environment variable issues in Next.js apps:
- Not restarting the dev server. Next.js reads
.env.localonce at startup. Any new variable requires a full restart. - Using
NEXT_PUBLIC_on a secret key. If the value is sensitive, it must never have that prefix. - Hardcoding fallbacks like
process.env.MY_KEY || 'default'. This silently hides missing variables in production. - Not adding variables to CI/CD. GitHub Actions cannot access
.env.local. Add all required values to repository secrets instead.
Security Checklist Before You Push
Before shipping, confirm each item:
.env.localis listed in.gitignore- No secret keys use the
NEXT_PUBLIC_prefix .env.exampleis committed to Git with blank values- Production variables are set in the platform dashboard, not committed to any file
- Every API key has the minimum permissions it needs
Frequently Asked Questions
- Why is
process.env.MY_VARundefined in my client component? - Environment variables are server-side only by default. To expose a variable to the browser, rename it with the
NEXT_PUBLIC_prefix and rebuild. Without that prefix, the value is stripped from the client bundle at build time. - What is the difference between
.envand.env.local? .envis committed to Git and shared across the team..env.localis git-ignored and holds personal or secret values that override.env. For secrets, always use.env.local.- Do I need to redeploy after changing environment variables in Vercel or Cloudflare?
- Yes. Both platforms inject variables at build time. Changing a variable in the dashboard takes effect on the next deployment, not immediately. Trigger a redeploy by pushing a commit or using the platform's manual redeploy button.
- Can people see my
NEXT_PUBLIC_variables in the browser? - Yes. Variables with the
NEXT_PUBLIC_prefix are inlined into the JavaScript bundle at build time and are fully visible in DevTools. Only use this prefix for values you are comfortable making completely public. - How do I use environment variables in GitHub Actions?
- Add your secrets to the repository under
Settings > Secrets and Variables > Actions. Reference them in your workflow file using${{ secrets.MY_SECRET }}. The.env.localfile is never available in CI, so every required variable must be added explicitly to the workflow or platform build settings.
Environment variables in Next.js are simple once the mental model clicks: server-side by default, client-side only when you opt in with NEXT_PUBLIC_, and never committed to Git when they contain secrets.
Keep a .env.example file up to date as your project grows. Set production values in the platform dashboard, not in committed files. Run through the security checklist before every deployment.
Related Articles
Husky + Prettier + lint-staged Setup for Next.js
Set up Husky v9, Prettier, and lint-staged in your Next.js project. Step-by-step guide covering pre-commit hooks with the correct 2026 config.
TypeScript 7 (Project Corsa): What Next.js Devs Need to Know
TypeScript 7 rewrites the compiler in Go for 10x faster builds. Here's what it means for your Next.js project and what to do right now.
How to Audit Your VS Code Extensions for Security
The GitHub breach happened through a VS Code extension. Here's how to check what you have installed and reduce your exposure in 10 minutes.