Skip to main content
Back to all posts

The Silent Killer of Startups: Leaked .env Files

SnapSend Team

Your .env file holds the keys to your kingdom: the database URL, the Stripe secret, the admin password, the AWS key that can spin up a hundred servers. It's a plaintext file that, by design, never leaves your machine — until one day it does. And when a .env leaks, you rarely find out from a polite email. You find out from a billing alert.

This post covers three things: how these files escape, how to stop them, and — the part most guides skip — how to hand a .env to a teammate without repeating the mistake in a different channel.

How a .env actually leaks

The classic path is git. You're in a hurry, you run git add ., the .env wasn't in .gitignore, and now your secrets are in a commit. Push it to a public repo and the clock starts.

This is not a hypothetical race. Automated scanners crawl new public commits within seconds, looking for filenames like .env and string patterns like AKIA... (AWS), sk_live_... (Stripe), or -----BEGIN PRIVATE KEY-----. Attackers aren't reading your code — bots are grepping it. A leaked cloud key can be picked up and used to launch crypto-mining instances before you've finished your coffee, and the resulting bill can reach five figures fast.

Git is the loudest offender, but it's not the only one:

  • Docker images. A COPY . . bakes the secret into a layer that anyone who can pull the image can extract.
  • Build logs and CI output. Echoing environment variables to "debug" writes them into logs that are often world-readable.
  • Error trackers. Uncaught exceptions frequently dump the full environment into Sentry or your logging pipeline.
  • Sharing "just this once" over Slack, email, or a Google Doc — where it then lives forever.

Prevention: keep it out of the repo

1. Ignore it globally, not just locally

Add .env (and friends like .env.local, .env.*.local) to your project .gitignore. Better, add it to your global gitignore so it's covered in every repo you touch:

git config --global core.excludesfile ~/.gitignore_global
echo ".env" >> ~/.gitignore_global

Then commit a .env.example with the keys but not the values, so new developers know what to fill in:

DATABASE_URL=
STRIPE_SECRET_KEY=
JWT_SIGNING_SECRET=

2. Scan before you push

A .gitignore only helps if the file was never tracked. Add a second net with a secret scanner. Tools like gitleaks or trufflehog run as a pre-commit hook and block the commit if they spot something that looks like a key:

gitleaks protect --staged

Wire this in with the pre-commit framework so it runs on every commit for the whole team, not just the one person who remembered to install it. If you're on GitHub, turn on Push Protection in the repository's security settings — it rejects pushes containing recognized secret formats server-side, catching what slips past local hooks.

3. Don't trust deletion

The most common mistake after a leak is thinking git rm .env && git commit fixes it. It does not. The secret is still in your git history, retrievable with git log -p. And even rewriting history with git filter-repo doesn't help once the commit has been pushed to a public host: it was already cloned, cached, and scanned. History surgery is for cleanup, not for containment.

If you already leaked: rotate first, panic later

There is exactly one correct first move, and it isn't deleting the commit. Rotate the credential. A leaked secret is compromised the moment it's public; the only thing that makes it harmless is invalidating it.

Work in this order:

  1. Rotate every secret in the file — not just the one you're worried about. Generate new keys in AWS, Stripe, your database, wherever, and revoke the old ones.
  2. Check for unauthorized use. Look at CloudTrail, billing dashboards, and access logs for activity you don't recognize.
  3. Then clean the history and fix the .gitignore so it can't recur.

Do it in that order every time. Cleaning history first just means the live keys stay valid for the extra ten minutes it takes you to rewrite the repo.

Sharing a .env with your team — the right way

Here's the workflow nobody documents. You've kept .env out of git perfectly. Now a new hire needs it on day one, or a contractor needs the staging config. You can't commit it, so how do you get it to them?

The wrong answers are the easy ones: emailing the file, pasting it into Slack, dropping it in a shared Drive folder. All three create a durable copy of your secrets in a system you don't control and can't reliably wipe. If that inbox or workspace is breached a year from now, your keys are still sitting there, still valid.

For anything permanent, the real answer is a secret manager — Doppler, Vault, 1Password, or AWS Secrets Manager — as the single source of truth, so nobody passes raw .env files around at all. We go deep on that layered approach in how DevOps teams share secrets.

But for the one-off human handoff — onboarding, a contractor, a quick "here's the staging config" — you want an ephemeral, end-to-end encrypted link that deletes itself after one read. That's exactly what SnapSend's encrypted file drop does:

  1. Drag your .env file in (or paste the contents as encrypted text if it's short).
  2. Set it to burn on read and give it a short expiry.
  3. Send the recipient the link.

The encryption happens in your browser — the key lives in the URL #fragment and never reaches the server, so SnapSend only ever stores ciphertext it can't decrypt. On first download, the file is destroyed. Even if the recipient's inbox is breached next month, there's no attachment waiting to be found — just a dead link. If you'd rather have them send you a config without installing anything, the secure receive flow inverts the same guarantee. You can read more about why one-time links beat durable copies in why ephemeral file sharing matters.

Common mistakes checklist

  • ❌ Committing .env because it wasn't in .gitignore — add it globally.
  • ❌ Deleting the file but leaving it in git history — the secret is still there.
  • ❌ Cleaning history before rotating keys — rotate first, always.
  • ❌ Baking secrets into Docker layers with COPY . . — use a .dockerignore.
  • ❌ Logging environment variables to "debug" — they end up in log storage.
  • ❌ Sharing the file over Slack or email — it becomes a permanent liability.

FAQ

Should I encrypt .env and commit the encrypted version? Tools like git-crypt or SOPS let you do this — a reasonable pattern for infra repos. Just make sure the decryption key is managed properly and never committed alongside it.

Is it safe to put secrets in environment variables at all? Yes — that's what they're for at runtime. The risk isn't the variable, it's the file leaking or being over-shared. Keep the file off git and out of chat.

What if I only need to share one value, not the whole file? Paste just that value into an encrypted one-time text link rather than shipping the entire .env. Least privilege applies to sharing too.

The takeaway

Leaked .env files rarely announce themselves. Keep them out of git with a global ignore and a secret scanner, rotate immediately if one escapes, and — when a teammate genuinely needs the file — hand it over with a link that self-destructs instead of an attachment that lives forever.

Next time you need to pass a .env to someone, drop it into SnapSend, set it to burn on read, and let the link delete itself once they have it.