BiTree
  • Search For Lessons
  • Curriculum
  • Pricing
  • For Educators
  • Become a Tutor
  • About
  • Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
BiTree

Live coding lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Search For Lessons
  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Become a Tutor
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 BiTree. All rights reserved.
Curriculum/Web Development/Security for Developers/Secrets Management and Environment Variables
45 minIntermediate

Secrets Management and Environment Variables

After this lesson, you will be able to: Keep API keys, database URLs, and tokens out of source code using environment variables and .env files, clean a leaked secret out of Git history, and manage secrets across environments and CI/CD.

A secret in your source code is a secret that will leak. This lesson covers environment variables, the .env file family, the .env.example pattern, .gitignore, what to do when a secret lands in Git history (it is compromised forever), platform secret dashboards, secrets in CI/CD, secret scanning tools, and key rotation.

Prerequisites:Git, GitHub, and Your First Repo

What an environment variable is and why secrets go there

An environment variable is a value the operating system hands to your process at startup, read in Node via process.env.MY_VAR. Secrets belong there, not in code, for one reason: code gets committed, shared, copied, and made public. The environment is per-deployment and never committed. The same code runs in dev, preview, and production with different secrets injected around it.

Before and after: hardcoded vs loaded from the environment

Never do the left. Always do the right. The key never appears in a file you commit.

tsx
// BEFORE (never do this): the key is now in Git history forever
const stripe = new Stripe("sk_live_51H8xqL...realkey...");
// AFTER: the key lives in the environment, not the repo
const key = process.env.STRIPE_SECRET_KEY;
if (!key) throw new Error("STRIPE_SECRET_KEY is not set");
const stripe = new Stripe(key);

The .env file family

.env holds defaults shared by everyone. .env.local holds your machine's real secrets and is never committed. .env.production holds production values (usually set in the platform dashboard instead of a file). .env.example is committed and contains every key with placeholder values, so a teammate cloning the repo knows exactly what to fill in. In Next.js, NEXT_PUBLIC_ prefixed vars are exposed to the browser; never put a secret behind that prefix.

The .env.example pattern and .gitignore

Commit the example, ignore the real file. Add the ignore rule before your first commit, not after.

tsx
# .gitignore (add BEFORE your first commit)
.env
.env.local
.env*.local
# .env.example (this one IS committed, with placeholders)
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxx
NEXTAUTH_SECRET=generate-with-openssl-rand-hex-32

💡 Once a secret is in Git history, it is compromised

Deleting the file in a later commit does not help. The secret still sits in history and anyone with the repo can check out the old commit and read it. The correct response is always: rotate the key immediately (assume it is burned), THEN clean history with git filter-repo (or the BFG Repo-Cleaner) to remove it from every commit, then force-push. Cleaning history without rotating is useless, because the secret was already exposed.

Cleaning a leaked secret out of history with git filter-repo

Rotate first, then scrub.

  1. 1

    1. Rotate the leaked key in the provider's dashboard right now. The old value is dead.

  2. 2

    2. Install git-filter-repo (pip install git-filter-repo).

  3. 3

    3. Run: git filter-repo --replace-text <(echo 'sk_live_realkey==>REDACTED')

  4. 4

    4. Force-push the rewritten history: git push --force.

  5. 5

    5. Tell collaborators to re-clone; their old clones still contain the secret.

  6. 6

    6. Confirm GitHub secret scanning no longer flags the repo.

Platform dashboards and secrets in CI/CD

Vercel, Railway, Render, and similar platforms let you set environment variables per environment (development, preview, production) in their dashboard. Set production secrets there, not in a committed file. In CI/CD, use GitHub Actions encrypted secrets (Settings > Secrets and variables > Actions) and reference them as ${{ secrets.MY_KEY }}. Never echo or console.log a secret in a pipeline; logs are often retained and visible to anyone with repo access.

Secret scanning and key rotation

GitHub secret scanning is on by default for public repos and alerts you (and often the provider) when a known key pattern is pushed. GitGuardian catches secrets before they are pushed via a pre-commit hook. truffleHog scans full history for entropy and known patterns. Rotate keys on a schedule even without a leak, and have a runbook for what to rotate the moment a key is exposed. Rotating without downtime usually means: issue the new key, deploy it, then revoke the old one once nothing uses it.

Quick Check

You accidentally pushed a real API key to a public GitHub repo. What is the FIRST thing you do?

Pick the best first action.

Common mistakes only experienced devs catch

Adding .env to .gitignore after the first commit (it is already tracked; you must also git rm --cached it). Putting a real secret behind NEXT_PUBLIC_ and shipping it to every browser. Committing .env.local because the .gitignore rule had a typo. Scrubbing history but forgetting to rotate, so the burned key is still live. Logging request headers (which include Authorization) at INFO level. Reusing the same secret across dev and prod, so a dev leak compromises production.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Security Is Your Job: The Developer's Security Mindset
Back to Security for Developers
Rate Limiting and Abuse Prevention→