The .env file that quietly rode into a public repo
One 'git add .' can commit your .env with live API keys, and bots harvest them within minutes. How to check your repos and rotate what leaked in ten minutes.
It’s the most ordinary command in the world. You’ve finished a change, you want to commit everything, so you type git add . and git push. The repository is public — it’s an open-source side project, or a demo, or a template you meant to share. Nothing feels risky about it.
Sitting in that folder, though, is a file called .env — the little text file where your app keeps its secrets: the API key for your payment provider, the database password, the token for your cloud account. git add . doesn’t know it’s special. It goes up with everything else, and the moment it lands in a public repository it stops being your secret. Automated bots continuously scan new public commits for exactly this, and a live key can be found and used within minutes of the push — sometimes faster than you’d finish reading this sentence.
That’s the quiet failure: not a hack, but a private file that walked out through the front door in plain sight, because the command that sent it treats every file the same.
Why it stays invisible
Two things conspire to keep this hidden.
The first is that nothing looks wrong. The push succeeds, the app keeps working, the key keeps working — because it’s a valid key, and now someone else has it too. There’s no error, no bounce, no alert. The first sign of trouble is usually a surprising bill from your cloud provider, or a rate-limit warning, weeks later, by which point the key has been quietly used for a while.
The second is that deleting the file doesn’t undo it. If you notice and remove .env in a later commit, the app looks clean now — but Git keeps history. The secret is still sitting in the previous commit, one click away in the repository’s timeline, for anyone who cares to look. And the bots have already copied it regardless: the instant a key touches a public repo you must assume a stranger has it, whether or not you tidy up afterwards.
Multiply that by every developer who’s ever run git add . in a hurry, every abandoned demo repo, every “I’ll make it private later,” and there’s a steady stream of live credentials reaching the open internet — 28.65 million new secrets on public GitHub in 2025 alone, by GitGuardian’s State of Secrets Sprawl 2026 count, a 34% jump on the year before.
Find it yourself
You can check your own exposure in about ten minutes, and you don’t need to be the developer to do the first part.
-
Search your public repositories for the file. On GitHub, go to your organisation, and in the search box type
org:yourorg filename:.env(swap in your org name). Anything that comes back is a.envcommitted somewhere it can be read. Do the same forfilename:.env.localandfilename:.env.production. -
Look at the history, not just the latest version. A repo that looks clean today may have committed a secret and removed it. On any suspect repo, open the file’s history (the History button, or
git log -- .envon your machine) and check whether an.envever existed. -
Turn on the platform’s own scanner. GitHub scans public repositories for known secret formats and will flag them for you. In each repository, go to Settings → Advanced Security (GitHub reshuffles these menus fairly often — this section was previously called Code security) and confirm Secret Protection, including its Push protection option, is enabled — push protection blocks a secret before it’s committed.
-
For a thorough sweep, run a scanner over the code. If you’re comfortable at a command line, a free tool such as
gitleaksortrufflehogwill scan an entire repository including its history in seconds —gitleaks detect --source .from inside the repo. This catches secrets that don’t sit in an obvious.env.
The usual reaction is a jolt at step 2 — not the file you knew about, but an old one you’d forgotten, still sitting in the history.
The fix
The order here matters more than anywhere else in this series, because one step is genuinely urgent.
Rotate first — treat any exposed key as burned. Before you tidy anything, go to whichever service the key belongs to and issue a new one, then revoke the old. This is the step that actually protects you: removing the file changes nothing if the key still works, because it’s already been copied. Rotate, then breathe.
Then remove it from history. Deleting the file in a new commit isn’t enough — the secret lives in the old commits. Use a tool such as git filter-repo or BFG Repo-Cleaner to strip it from the whole history, then force-push. If that’s beyond you, it’s a reasonable thing to hand to a developer; the rotation above has already defused the danger, so this part isn’t a race.
Stop the next one at the source. Add .env (and .env.*) to a .gitignore file in every project, so git add . can’t sweep it up again. Turn on push protection, which refuses the commit the moment it spots a secret — the cheapest safety net there is, because it works before the mistake leaves your machine.
Keep real secrets out of files entirely where you can. For anything that matters, a secrets manager or your host’s environment-variable settings keeps the key out of the codebase altogether, so there’s nothing in the folder to leak.
None of this needs a security team. It needs one .gitignore line, one setting turned on, and the habit of rotating without hesitation the moment a key gets out — because the only real defence is assuming it’s already gone.
If you connect a lot of third-party apps and worry about the access they hold, the companion issue on OAuth grants covers the other half of this problem — the keys you handed out on purpose and forgot.
One real, fixable exposure every week. Free.