Why it keeps happening
.env files are plain text in the project folder, next to everything you do want to commit. They leak in a few ordinary ways:
- The project started without a
.gitignore, or with one that covers.envbut not.env.localor.env.production. - The file was committed once, before the ignore rule, so git still tracks it.
git add .or an AI coding tool staged everything in the folder.- The folder was zipped, shared or copied somewhere public.
GitGuardian counted 28.65 million new secrets in public GitHub commits in 2025 (State of Secrets Sprawl 2026).
Check .gitignore
Make sure every variant is covered, and that .env.example is still allowed:
# .gitignore
.env
.env.*
!.env.example
Then ask git whether it’s already tracking one:
git ls-files | grep -E '(^|/)\.env'
If that prints anything other than .env.example, stop tracking it (the file stays on disk):
git rm --cached .env
Commit names, not values
A .env.example with the names and no values tells the next person (or the next machine) which variables the project needs:
STRIPE_SECRET_KEY=
RESEND_API_KEY=
DATABASE_URL=
Block commits with a key in them
A pre-commit hook can check staged changes before each commit. General-purpose scanners such as gitleaks look for things shaped like keys. Slinky’s check looks for exact copies of keys you’ve saved, so it doesn’t need to guess.
If a key was already pushed
- Make a new key on the service and turn the old one off. This is the step that matters.
- Update every place that used the old key.
- Then, if you like, clean the history with a tool such as
git filter-repo. Forks and clones keep the old copy either way.
How Slinky does it
Slinky gives each project’s variables a home outside the folder:
- Drop the project folder on Slinky. It reads the
.envfiles (apps inside the folder too), shows Local, Preview and Production side by side, and warns about any file git could commit. - Move into Slinky. One click saves the variables, writes a
.env.examplewith names only, and puts the.envin the Trash. - Run without a file.
slinky run -- npm run devstarts your app with the variables after Touch ID.slinky hook zshcan load them when youcdinto the project and unload them when you leave. - Stop Commits with Secrets. In a project’s Import menu, or
slinky guard installfrom Terminal, adds a git hook that checks each commit for your saved values. - Make New Key. Slinky opens the service’s page for making keys, and when you paste the new one, every project using the old key gets it.