Environment Variables and Secrets
Keep database passwords and API keys out of your code by storing them in a .env file — and learn which values are safe to send to the browser.
What you will learn
- Store secrets in a .env file and read them with process.env
- Explain why secrets must never be hard-coded
- Use the NEXTPUBLIC prefix only for values that are safe in the browser
Why not just put the key in the code?
Apps need secret values: a database password, a paid API key, a login secret. If you type these directly into your code and push to GitHub, anyone who sees your code sees your secrets — they could run up huge bills or steal your data. The fix is to keep secrets in a separate file that is never committed to Git: a .env file.
The .env file
An environment variable is a named value that lives outside your code, in the environment your app runs in. You list them in a file called .env in your project root, one NAME=value per line.
# .env (never commit this file)
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_live_51HxYz...Note: Output (behaviour): Next.js automatically loads this file when it starts. The values are now available to your code, but the file itself stays on your machine and on your server — it is listed in .gitignore so it never reaches GitHub.
Reading a variable with process.env
In server code (a Server Component, Route Handler or Server Action) you read a value through the special process.env object, using the same name you wrote in the file.
// app/api/charge/route.js (server only)
export async function POST() {
const key = process.env.STRIPE_SECRET_KEY; // read the secret
// ...use key to talk to Stripe...
return Response.json({ ok: true });
}Note: Output (behaviour): process.env.STRIPESECRETKEY gives you the value from the .env file. Because this code runs on the server, the secret is used there and never sent to the browser — exactly what you want for a secret key.
Server-only vs browser-safe: the NEXTPUBLIC rule
This is the one rule beginners must get right. By default, environment variables are server-only — they are never sent to the browser, which is perfect for secrets. But sometimes you need a value in the browser (like a public analytics ID that is not secret). To allow that, and only then, prefix the name with NEXT_PUBLIC_.
# .env
DATABASE_URL=postgresql://... # server-only (secret)
NEXT_PUBLIC_SITE_NAME=CodingClave # safe to expose to the browserNote: Output (behaviour): process.env.DATABASEURL works only in server code; in the browser it is undefined (kept secret). process.env.NEXTPUBLICSITENAME works everywhere, including Client Components — because you opted in by adding the NEXTPUBLIC prefix.
| Name | Where it works | Use for |
|---|---|---|
DATABASE_URL | Server only | Secrets: DB passwords, private API keys |
NEXT_PUBLIC_SITE_NAME | Server and browser | Non-secret values the browser needs |
Adding and using a secret, step by step
Here is the whole process of bringing a new secret into your app safely:
- Create a
.envfile in your project root if you do not have one, and confirm.envis listed in.gitignoreso it is never committed. - Add the secret as a line, with no
NEXT_PUBLIC_prefix, for exampleSTRIPE_SECRET_KEY=sk_live_.... - Restart the dev server (
npm run dev) so Next.js loads the new value. - In server code only — a Server Component, Route Handler or Server Action — read it with
process.env.STRIPE_SECRET_KEY. - When you deploy, add the same name and value in your host dashboard (on Vercel: Settings → Environment Variables), since the
.envfile does not travel with your code.
Watch out: Never put a secret behind NEXT_PUBLIC_. Anything with that prefix is baked into the JavaScript sent to every visitor, so a NEXT_PUBLIC_STRIPE_SECRET_KEY would hand your secret key to the whole world. Secrets stay without the prefix.
Tip: When you deploy, your .env file does not go with it. Add each variable in your host dashboard instead — on Vercel that is Settings → Environment Variables, which keeps production secrets safe on the server.
Q. You have a secret database password. How should it be named in .env so it is NOT exposed to the browser?
✍️ Practice
- Create a
.envfile with one server-only variable and read it in a Route Handler withprocess.env. - Add a
NEXT_PUBLIC_variable and show it in a Client Component.
🏠 Homework
- Move a hard-coded API key from your code into a
.envfile, read it viaprocess.envin server code, and confirm it is not visible in the browser.