← All news
security5 min read

Three .env file mistakes that leak your secrets

By Seaworthy · June 12, 2026

Red padlock on a black computer keyboard, symbolizing password and secrets security

Photo by FlyD on Unsplash

Most leaked credentials I have seen in the wild did not come from a sophisticated attack. They came from a .env file that ended up somewhere it should not be. The attack surface is boring and preventable, which is exactly what makes it so common.

Here are the three mistakes worth knowing about.

Mistake 1: committing .env to git

You scaffolded a project, created a .env file with real credentials, and ran git add .. The file is now in your git history forever, even if you delete it on the very next commit.

This is the part people miss. Git does not track files, it tracks snapshots. When you delete a file and commit, the new snapshot no longer contains it. But the old snapshot still does. Anyone who clones your repo and runs:

git log --all --full-history -- .env

...will see every commit that touched .env. Then:

git show <commit-hash>:.env

...gives them the full contents of the file at that point. Real API keys, database passwords, whatever you had in there. No special tools needed, just git.

The only real fix is to rewrite history with something like git filter-repo:

pip install git-filter-repo
git filter-repo --path .env --invert-paths

This removes the file from every commit. If the repo is on GitHub, you need to force-push and then contact GitHub support to purge their cache. Even then, anyone who cloned before you cleaned up still has the original history locally.

So the honest answer is: once a secret has been committed and pushed to a shared or public repo, assume it is compromised. Rewriting history is good hygiene, but the mandatory step is rotation.

Mistake 2: missing .gitignore entry

You know about this one. You still forget it.

The .gitignore entry for .env should be in every project, from day one, before you create the file. The failure mode is always the same: you add .env to .gitignore after you have already committed it. At that point the file is tracked, and .gitignore has no effect on tracked files. Git will keep including it in every future commit.

To stop tracking a file that is already in git:

git rm --cached .env
git commit -m "stop tracking .env"

The file stays on disk. It just stops appearing in future snapshots. You still need to deal with the history if the file contained real secrets.

A .gitignore that actually covers this looks like:

.env
.env.local
.env.*.local
.env.production

The glob .env.* covers a lot, but be careful not to exclude .env.example if you use that pattern to document required variables. You want the example committed; you do not want the real one to be.

Mistake 3: sharing .env files over Slack or email

This one feels less like a mistake because it works immediately. You need a colleague set up quickly, you paste the .env into Slack, they copy it, done.

The problem is that Slack indexes message content. Your secrets are now sitting in a searchable message archive that every current and future employee can access, that gets exported during legal holds, and that Slack itself can read. Email is worse: your .env might be stored on multiple mail servers, in multiple backups, and in your colleague's inbox indefinitely.

If your company's Slack workspace gets compromised, or if someone with access to message history leaves and still has a local copy, those credentials are out. Slack and email are communication tools, not secret stores.

The actual fix is a secrets manager. If that feels like too much infrastructure, a password manager with secure sharing (1Password, Bitwarden) works fine for small teams. The point is that the tool should have access controls, audit logs, and the ability to revoke access. Slack has none of those things for pasted text.

What to do when you find a leaked secret

Check whether the secret is already in your history:

git log --all -p | grep -i "API_KEY\|SECRET\|PASSWORD\|TOKEN" | head -40

That is noisy but fast. More targeted tools like truffleHog or gitleaks scan commit history properly and can identify patterns across many secret types:

gitleaks detect --source . --log-opts="--all"

If you find something, the steps are:

  1. Rotate the credential immediately. Do not wait until after cleanup.
  2. Audit the service's logs for any unauthorized use during the exposure window.
  3. Clean the history with git filter-repo.
  4. Force-push and notify your hosting provider to purge caches.
  5. Add the file to .gitignore before creating a replacement.

Rotation is non-negotiable and should happen before anything else. Cleaning history without rotating is wishful thinking: you have no way to know who already has a copy.

The underlying pattern

All three mistakes share a root cause: treating secrets like code. Code lives in git, gets emailed around, gets pasted into chat. Secrets need different handling, and the tools for that handling (secrets managers, environment injection, .gitignore) are not hard to use.

The friction of doing it right is low. The cost of not doing it, once a credential is out in the wild, can be severe.

Seaworthy catches this class of issue automatically, including secrets already baked into git history.

This article was generated by AI and summarises publicly available sources.

From the makers of Seaworthy

Seaworthy scans your repo for the issues covered in articles like this one.

Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.

$ npx seaworthycode