Keep secrets out of Git: a practical guide for developers
Git is optimized for sharing history. That makes it the wrong place for API keys, database passwords, and private signing keys. Yet almost every stack still teaches “copy .env and run” — so teams need boring, reliable guardrails.
This guide is actionable: copy-paste configs, commands, and a decision tree for when something has already been committed.
1. Golden rules
- Never commit real secrets — even “disabled” keys or “old” tokens.
- Assume CI logs are public — mask variables, never print secrets.
- Treat history as immutable until rewritten — deleting a file in a new commit does not remove past blobs.
- Rotate after doubt — if a secret might have leaked, revoke and re-issue; cleaning Git is not enough.
OWASP: Secrets Management Cheat Sheet.
2. .gitignore patterns that actually work
Baseline for Node / full-stack repos
# Local env files
.env
.env.*
!.env.example
!.env.sample
!.env.template
# Tooling
.direnv/
.envrc
# macOS
.DS_Store
Common gotcha: alternate names
Developers create env.local, local.env, credentials.json, secrets.yml. If you standardize on one approach, document it in CONTRIBUTING.md and ignore the rest explicitly after team agreement.
Verify what Git is tracking
git ls-files | rg -n "\.env|credentials|secret" || true
If you see .env, it is tracked — remove it from the index (see section 5).
3. Commit only templates
.env.example (committed):
# Required for local API
DATABASE_URL=postgresql://USER:PASSWORD@localhost:5432/app_dev
# Get a test key from the Stripe dashboard (test mode only)
STRIPE_SECRET_KEY=sk_test_replace_me
# Generate: openssl rand -base64 32
SESSION_SECRET=replace_me
.env (never committed): real values filled in by each developer.
Document where developers obtain each secret (internal wiki, admin UI, SSO portal).
4. Block secrets before they enter history
Option A: gitleaks locally
Install and run:
gitleaks detect --source . -v
Wire into pre-commit (see gitleaks).
Option A2: trufflehog (filesystem + Git history)
TruffleHog can scan repositories and historical commits for verified secrets. Many teams run both gitleaks and trufflehog in CI because overlap is cheap compared to incidents.
# Example: verify current CLI flags in upstream docs before relying on this in CI
trufflehog filesystem . --only-verified
Option B: GitHub push protection
For GitHub-hosted repos, enable secret scanning and push protection so high-confidence patterns are blocked at push time. See Secret scanning.
Option C: CI scan on pull requests
Even with local hooks, CI should scan the merge result — laptops can skip hooks with --no-verify.
5. Oops — .env was tracked. Now what?
Step 1: stop the bleeding
Rotate every credential that appeared in the file — before or in parallel with history cleanup. Assume the worst if the repo was ever public or widely cloned.
Step 2: remove from the index (not just disk)
git rm --cached .env
git commit -m "Stop tracking .env (secrets were rotated)"
This stops future commits from tracking the file; it does not remove historical blobs.
Step 3: purge history (coordination required)
GitHub documents using git-filter-repo to remove sensitive data:
Removing sensitive data from a repository
High level:
- Install
git-filter-repo. - Rewrite history to strip paths or replace strings.
- Force-push protected branches with team coordination.
- Invalidate cached forks and local clones — everyone must re-clone or hard-reset carefully.
Detailed playbooks and provider-specific notes: I accidentally committed an API key — what now?.
6. Monorepos and generated config
In monorepos, secrets sometimes land in:
apps/web/.envpackages/config/local.json- Terraform
*.tfvars(often should be gitignored or use.tfvars.example)
Run a periodic audit:
git grep -n "sk_live\|BEGIN RSA PRIVATE KEY\|AKIA" || true
7. Where PassStore fits
.gitignore protects Git, not your laptop. A local-first vault such as PassStore gives you a dedicated place for developer secrets — encrypted at rest, with Keychain integration options — so the “source of truth” for your keys is not fifteen plaintext files scattered across ~/Projects.
Read the technical summary: Security overview.