Quick Tip: Keep Secrets Out of Git for Good

Quick Tip: Keep Secrets Out of Git for Good

Committing a secret to Git is one of those mistakes that feels harmless right up until it isn’t. An API key, a database password, or a .env file slips into a commit, gets pushed, and now it lives in your repository history forever — even if you delete it in the next commit. Here is a quick, practical habit to keep secrets out of Git for good.

Start every project with a .gitignore

Before your first commit, add a .gitignore that excludes the usual culprits:

.env
.env.local
*.pem
*.key
secrets.json
node_modules/

The single most valuable line there is .env. Keep your real configuration in a git-ignored .env file, and commit a .env.example with the keys but not the values, so teammates know what to set without ever seeing your credentials.

If a secret already slipped in

Deleting it in a new commit is not enough — it is still in history. Rotate the exposed credential immediately (assume it is compromised), then scrub it from history with a tool like git filter-repo or BFG (a history rewrite, in the same family as an interactive rebase). Rotation is the part people skip, and it is the part that actually protects you.

Add a safety net

Humans forget, so let a tool watch your back. A pre-commit hook using something like gitleaks or git-secrets scans staged changes and blocks the commit if it spots something that looks like a key. It takes five minutes to set up and has saved countless developers from a very bad afternoon.

How fast does a leaked key get exploited? Faster than you think

If you suspect a pushed secret is “probably fine because nobody saw it,” here’s the uncomfortable reality: automated scanners watch the public GitHub event feed continuously, harvesting anything that looks like a credential. Security researchers have run honeypot experiments — pushing deliberately-planted AWS keys to a public repo — and observed the first unauthorized use within minutes. Not days. Minutes. Cloud providers now run their own scanners too, which is why AWS sometimes emails you about an exposed key before you’ve noticed it yourself.

The lesson: the moment a secret touches a public commit, the race is already lost. There is no “quickly delete it before anyone notices.” There is only rotation. Private repos buy you more time, but insider risk, future repo visibility changes, and CI logs mean the same rule applies — just with less urgency panic.

Setting up gitleaks in five actual minutes

Since the safety net is the step everyone postpones, here’s the whole setup. Install gitleaks (a single binary — brew install gitleaks on macOS, or grab the release binary on Linux), then wire it into a pre-commit hook:

# .git/hooks/pre-commit (make it executable)
#!/bin/sh
gitleaks protect --staged -v
if [ $? -ne 0 ]; then
  echo "gitleaks found a potential secret. Commit blocked."
  exit 1
fi

For a team, put it in CI as well, because hooks are per-machine and someone will always have a fresh clone without them: a one-line job running gitleaks detect fails the pipeline if a secret lands. The pre-commit hook protects individuals; the CI check protects the repo. You want both, and together they take less time to set up than reading this article did.

Scrubbing history when prevention already failed

Rotation makes a leaked key useless, but you may still want the secret gone from history — repos get forked, cloned, and audited. The modern tool is git filter-repo (the older filter-branch is deprecated and painfully slow):

# Remove a file from ALL history
git filter-repo --path .env --invert-paths

# Or replace a specific leaked string everywhere
echo 'sk_live_abc123==>REMOVED' > replacements.txt
git filter-repo --replace-text replacements.txt

Then force-push and — this is the part people miss — have every collaborator re-clone, because their local copies still hold the old history and a casual git push from a stale clone resurrects it. Also check the platform side: GitHub caches commits reachable from pull requests even after force-pushes, so for a serious leak you contact support to purge those. All of which reinforces the real moral: history surgery is disruptive enough that the five-minute prevention setup is a bargain.

Where secrets should live instead

Saying “not in Git” begs the question of where they do belong, and the answer scales with your project. Locally: the git-ignored .env file, loaded by your framework or a dotenv library. In CI: the pipeline’s encrypted secrets store — GitHub Actions secrets, GitLab CI variables — which inject values as environment variables at build time without ever writing them to the repo. In production: your platform’s configuration UI (Vercel, Railway, Heroku) or, for larger teams, a dedicated secret manager like Vault or AWS Secrets Manager, which adds rotation schedules, access auditing, and per-service permissions.

The common thread across every tier: the secret is attached to the environment that needs it, not to the code that uses it. Code describes which secrets it needs (a .env.example, a config schema); environments supply the actual values. Keep that separation and the “oops, pushed a key” category of incident becomes structurally impossible rather than a matter of daily discipline.

Frequently asked questions

Is it safe to put secrets in a private repo? Safer, not safe. Access changes over time — new collaborators, org transfers, accidental visibility flips, leaked CI logs. Treat “private” as a speed bump, not a vault; the .env-plus-gitignore discipline costs nothing and works in both worlds.

What about secrets in old commits of a repo I’ve made public? Assume every credential in the entire history is compromised the moment the repo goes public — scanners read history, not just HEAD. Rotate everything first, then publish.

GitHub sent me a “secret scanning” alert — now what? Rotate the credential immediately, then clean history if practical. The alert means GitHub’s scanner matched a known credential format, and if GitHub found it, assume less friendly scanners did too.

Keeping secrets out of Git comes down to three habits: ignore them by default, rotate anything that leaks, and let a scanner catch what you miss. Build those in early and you will never have to write that awkward “please rotate the key I just pushed” message.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *