76  76. Step 3: Storing your private info (env files, etc)

As of Oct 22, 2026 · Official docs: Next.js environment variables

Private information such as passwords and API keys (which we discuss later) should never be stored in git, because git repositories are often shared with other people. For the same reason, passwords and API keys should never be typed directly into JavaScript code, or into code in any other programming language.

A typical web project connects to several outside services, and each one needs some “secret” information (a password, an API key, etc.) to authenticate (log in) to it. In this project, your Next.js app works with Supabase, Google sign-in (Google OAuth), the OpenAI API and Stripe.

76.1 Where each setting goes

Not every setting lives in the same place:

Setting Where it goes When you set it
Your Supabase project’s URL and publishable key .env.local, a file on your computer This step and Step 5: Setup Supabase
The OpenAI API key and the Stripe keys, which the Edge Functions use Supabase’s servers, as secrets (npx supabase secrets set ...) Step 7: Set Up OpenAI API and Step 8: Set Up Stripe
The Google Client ID and Client Secret The Supabase dashboard Step 6: setup authentication (Google OAuth)
The same two settings as in .env.local, for the live site Vercel’s website (Environment Variables) Step 13: Deploy to Production
The settings for the automated tests (optional) .env.test.local, a file on your computer Step 15: Automated Testing and Troubleshooting

In this step you create just one file, .env.local.

76.2 Create .env.local

The project comes with a file named .env.example. It is a template: it lists the settings the app needs, with fake values that contain XXXX, and it starts with notes (lines that begin with #) that explain the file. It contains no real information, so it is safe for it to be in the repository.

In the terminal, cd to the project’s top-level folder (the one with package.json in it) and copy the template:

cp .env.example .env.local

cp copies the first file to a new file with the second name. The name must be exactly .env.local, dots included. You fill in the real values in Step 5: Setup Supabase.

Files whose names start with a dot are hidden: a plain ls doesn’t list .env.local, but ls -a does. In VS Code’s file list, it appears as usual.

The project’s .gitignore file lists the files that git should never commit. One of its lines is .env*.local, which matches every file whose name starts with .env and ends with .local: .env.local, .env.test.local and so on. So your real values stay on your computer.

The templates .env.example and .env.test.example don’t end in .local, so they are in the repository. That’s why they must only ever hold fake values.

Is anything in .env.local secret?

Both settings in .env.local start with NEXT_PUBLIC_. Next.js builds those into the web pages that every visitor’s browser downloads, so they are meant to be public. That is why the publishable key goes here, and the secret keys never do: a secret key in .env.local could end up in a web page.

76.3 Older versions of this tutorial

The tutorial video and the original tutorial also copy .env.example to supabase/.env. You don’t need that file: the Edge Functions get their keys from Supabase secrets, which you set with npx supabase secrets set.

Practice Questions

Easy

Question 1 · True or false

It’s fine to store passwords and API keys in your git repository, so that your teammates can find them easily.

False. Secrets should never be stored in git, because git repositories are often shared with other people. For the same reason, they should never be typed directly into your code either.

Question 2 · Multiple choice

Where should you keep an API key for this project while you develop on your computer?

  1. Typed directly into a JavaScript file
  2. In a .env file, such as .env.local, that is listed in .gitignore
  3. In the project’s README, so teammates can find it
  4. In .env.example

b. .env files hold the secrets, and because they are listed in .gitignore, git never commits them. A key typed into code (a) or into the README (c) ends up in git and is shared with everyone who can see the repository. .env.example (d) is part of the repository, so it must contain only example values, never real ones.

Question 3 · Short answer

The page says each integration needs “secret” information to authenticate to the remote system. What does “authenticate” mean here, and what kinds of secret information are used?

To authenticate means to prove who you are, basically to log in, to the other system (Supabase, Google, OpenAI, …). The secret information is a password, an API key or something similar. Whoever has it can act as you on that service, which is why it must be kept secret.

Question 4 · Short answer

What is the .env.example file for?

It is an example (template) file created by the project’s developer. It shows which settings the project needs, but it doesn’t contain real information. You copy it to create your real .env files, and later you fill in your own keys.

Question 5 · Short answer

What does the .gitignore file do, and how does it protect your .env files?

.gitignore lists the files in the project that should never be committed to git. Because the .env files are referenced in .gitignore, git ignores them, so your secrets stay on your computer and out of the shared repository.

Medium

Question 6 · Write the command

You are in the project’s top-level folder. Write the command that creates the file .env.local from the template.

cp .env.example .env.local

cp copies a file: the first name is the original, the second is the new copy.

Question 7 · Short answer

Match each setting with the place where it goes.

Setting Place
1. Your Supabase URL and publishable key, to run the app on your computer A. Supabase secrets (npx supabase secrets set)
2. The OpenAI API key that an Edge Function uses B. .env.local
3. The Supabase secret key that the automated tests use C. Vercel’s Environment Variables
4. Your Supabase URL and publishable key, for the live site D. .env.test.local

1 → B, 2 → A, 3 → D, 4 → C.

The Edge Functions run on Supabase’s servers, so their keys are stored there (2). The live site runs on Vercel’s servers, which never see your .env.local (4). The tests’ secret key goes only in .env.test.local (3), never in .env.local.

Question 8 · True or false

.env.example contains your real API keys, so it must never be committed to git.

False. .env.example contains only example values, no real information. It is part of the repository (it came with the code you cloned), so that everyone can see which settings they need to fill in. Your real values go into .env.local (and, for the tests, .env.test.local), which are not committed.

Question 9 · Scenario

Maria runs cp .env.example .env.local and then ls, but .env.local isn’t in the list. Did the command fail?

Probably not. Files whose names start with a dot are hidden, and a plain ls doesn’t show them. ls -a shows hidden files too, including .env.local. (Also, cp prints nothing when it succeeds; an error message would mean that something went wrong.)

Hard

Question 10 · Find the bug

Sam typed:

cp .env.example .env.locale

What’s wrong, and why could it cause two different problems?

The file name is wrong: it must be exactly .env.local.

  1. The project looks for .env.local, so it won’t find the settings Sam puts in his misnamed file.
  2. The .gitignore rules may not cover a file named .env.locale, so if he later commits everything, his secrets could end up in git.

Fix: rename the file with mv .env.locale .env.local.

Question 11 · Scenario

Sam is in his home folder when he runs cp .env.example .env.local. He gets an error saying that .env.example doesn’t exist (“No such file or directory”). Why, and how does he fix it?

.env.example and .env.local are relative paths, so they are looked up starting from the current working directory: his home folder, where those files don’t exist. As the page says, he must first cd into the project’s top-level folder (for example cd ~/projects/full-stack-demo-app/task-app) and then run the command again.

Question 12 · Scenario

Two weeks later, Maria has filled in .env.local with her real keys. To be “safe”, she runs cp .env.example .env.local again. What happens? Can git help her undo it?

cp overwrites an existing file without asking, so her real keys are replaced by the example values. Git can’t bring them back: .env.local is listed in .gitignore, so git never saved a copy of it. She has to enter the values again, from her setup notes or by getting them again from each service’s website. Lesson: copy .env.example only once, when you first create the files.

Question 13 · Long answer

Why should secrets never be typed directly into your code? What does keeping them in .env files give you instead?

Code files are committed to git and shared: with teammates, on GitHub, and sometimes with the whole world. A key typed into code is therefore effectively published. Keeping secrets in .env files means:

  • the real values live only in files that .gitignore keeps out of git;
  • the code itself can be shared safely, because it contains no secrets;
  • each person (or computer) can use their own keys without changing the code.

Question 14 · Scenario

Sam wants his automated tests to work, so he pastes his Supabase secret key into .env.local as NEXT_PUBLIC_SUPABASE_SECRET_KEY="sb_secret_...". .env.local is never committed to git, so he thinks the key is safe. Why is he wrong, and where should the key go?

Git isn’t the only way a value can leak. Next.js builds every setting whose name starts with NEXT_PUBLIC_ into the web pages that visitors’ browsers download, so anyone could read the secret key there, and it skips all of the database’s security rules. The tests’ settings belong in .env.test.local, under the name the tests expect (SUPABASE_SECRET_KEY, without NEXT_PUBLIC_). Since the key may already have leaked, he should also create a new secret key in the Supabase dashboard and delete the old one.

© 2025-2026 Y. Rosenthal. All rights reserved.