← all posts

Environment variables in your repo: the gift that keeps taking

·2 min read ·Security · DevOps · Best Practices

I've seen developers commit .env files to their repository, realize it immediately, delete the file, and commit again. They think they've fixed it. They haven't. The secret is in the git history forever.

Git history is a feature, not a log that gets cleaned up. Everyone who clones that repository can see every commit, including the one with the secret.

What Actually Happens

  1. Secret committed
  2. Developer realizes it and deletes the file
  3. Secret is still in the history at the commit where it was added
  4. GitHub or GitLab scans for patterns matching API keys and notifies the service provider
  5. Service provider revokes the key and emails the account owner
  6. If the key wasn't revoked in time, someone may have already used it

This happens. The scanners are fast.

The Correct Setup

# .gitignore — always, from day one
.env
.env.local
.env.production
# .env.example — committed to repo
APP_KEY=
DB_HOST=127.0.0.1
DB_PASSWORD=
STRIPE_SECRET_KEY=

The .env.example file shows which variables are required without containing real values. New developers copy it and fill in their own values.

If You've Already Committed a Secret

Revoke it immediately. Assume it's compromised. Then clean the history:

# Use git-filter-repo (preferred) or BFG Repo Cleaner
git filter-repo --path .env --invert-paths

Force-push after. Notify everyone who has cloned the repo to re-clone.

Production Secrets

Production secrets don't go in any file that touches the codebase. They go in environment variables set by your hosting platform, or a secrets manager (AWS Secrets Manager, HashiCorp Vault). The codebase reads them with env() or getenv(). No file. No commit. No accident.