# Your .env file is probably already in your Git history. The 15-minute audit (and the 5 habits that stop new leaks for good).

> Source: <https://dev.to/chalom_ellezam_5989bce65e/your-env-file-is-probably-already-in-your-git-history-the-15-minute-audit-and-the-5-habits-that-5dkg>
> Published: 2026-06-15 18:21:48+00:00

*Disclosure: I am a senior backend tech lead in Paris and I run HostingGuru, a small European PaaS. This article mentions HostingGuru once near the end. Everything else works regardless of where you host.*

A founder I advise forwarded me a GitHub email last Sunday. Subject line: "We found a secret in one of your repositories." It was an OpenAI key, six commits old, in a repo she had flipped from private to public on Friday so a designer could browse it. The key was still active. She had been billed €240 in API calls between Friday evening and Sunday morning by someone running what looked like a CSV-to-embeddings job out of a residential IP block in Brazil.

This is the most common preventable production incident I see in indie SaaS. Not a database outage, not a Stripe bug, not a deploy gone wrong. A key in a place it should not be. Usually a `.env`

file committed before `.gitignore`

was set up. Sometimes a hardcoded `OPENAI_API_KEY = "sk-..."`

in a Python script the founder forgot they pushed during early prototyping. Almost always findable in two minutes if you know where to look.

If you have ever pasted an API key into Claude Code, Cursor, Lovable, or Bolt, or if you have ever committed a project before you knew what `.gitignore`

did, this article is the audit you have been putting off.

Three things changed. AI coding tools often hardcode keys directly, because the model is trying to make the snippet work in front of it. The number of repos a solo founder touches per year has roughly tripled since vibe-coding caught on. And the providers each app talks to (Stripe, OpenAI, Anthropic, Resend, Twilio, Supabase, Cloudflare R2) have gone from two or three to ten or fifteen.

More repos times more providers times more "just paste it in" equals more leaks. The audit is cheap, the prevention is mostly free.

This works on any repo. Run it on every repo you have pushed.

A common mistake is to `grep`

for keys in current files. If you committed a secret six months ago and deleted it, it is still in history. Anyone who clones the repo gets it.

The tool I reach for is `gitleaks`

. Install it once (`brew install gitleaks`

on macOS, or grab a binary from GitHub).

```
cd ~/code/your-app
gitleaks detect --source . --verbose
```

This scans your entire commit history with a set of regex rules that match common provider patterns (Stripe, GitHub, AWS, OpenAI, Anthropic, Slack, and many more). On a typical indie project I have audited, this finds something in 6 cases out of 10. The classic findings are an old `.env`

from before the project had a `.gitignore`

, a forgotten `test.py`

with a hardcoded key, or a `docker-compose.override.yml`

that someone committed by accident.

If `gitleaks`

reports nothing, run `trufflehog`

as a second opinion. It has slightly different heuristics:

```
trufflehog filesystem --directory=. --no-update
```

If the repo lives on GitHub, GitHub itself has been scanning your pushes for known secret formats since 2023. Even on free private repos. Go to your repo, Security tab, Secret scanning alerts. Anything GitHub already caught will be listed there with the exact commit and line.

If you see anything there, treat it as real. GitHub notifies the provider (Stripe, OpenAI, etc.) so the key may be auto-revoked, but do not rely on that. Some providers do, some do not.

This is the part people skip because it is annoying. You log in to each provider, generate a new key, swap it in your hosting platform's env var settings, redeploy, and verify the old key is dead.

A habit that helps: keep a `secrets-rotation.md`

in a private vault listing every provider, the last rotation date, and the URL of that dashboard's "API keys" page. When you audit, you walk down the list. The hardest part of rotation is not the rotation itself, it is remembering all the places a key lives.

For OpenAI and Anthropic specifically, do not just create a new key and leave the old one active. Revoke the old one explicitly. A leaked key that has been rotated but not revoked is the same as a leaked key.

If your repo is private and you trust everyone with read access, rotating is enough. The leaked key is dead, anyone who finds it later gets a 401.

If your repo is public, or was public at any point, or you simply do not want a paper trail of "things I shipped that I should not have," you can rewrite history with `git filter-repo`

:

```
pip install git-filter-repo
git filter-repo --invert-paths --path .env
git push --force --all
```

This rewrites every commit and force-pushes. Do not do this on a repo where other people have open branches without warning them. For a solo project, it is fine and takes about 30 seconds.

A note: even after a force push, GitHub keeps orphan commits accessible by SHA for a while. The only real fix for a leaked credential is rotation. Rewriting history is cosmetic.

Auditing is the fire drill. Habits are how you stop the next fire.

`.gitignore`

is the first file in every new repo
Before you write any code, before the first commit, the first file is `.gitignore`

and it includes `.env`

, `.env.local`

, `.env.*.local`

, and anything provider-specific like `firebase-adminsdk-*.json`

or `gcp-key.json`

.

If you use AI coding tools, add this to your starter prompt or to your `CLAUDE.md`

/`.cursorrules`

: "Never write secrets or API keys directly in source files. Always read them from `process.env.X`

or `os.getenv('X')`

. Never commit `.env`

or `.env.local`

." Models will mostly respect this. They will occasionally forget, which is why you also need habit 2.

`gitleaks`

The cheapest possible defense. Install `pre-commit`

(`pip install pre-commit --break-system-packages`

) and drop a `.pre-commit-config.yaml`

in your repo:

```
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
```

Then run `pre-commit install`

once. From that point on, every `git commit`

runs `gitleaks`

against the staged diff. If you paste a key into a file by accident, the commit is blocked.

This single setup has saved at least four founders I work with from an embarrassing rotation. It takes 90 seconds to install and you forget it exists.

Every modern hosting platform (Vercel, Fly, Railway, Render, HostingGuru, you name it) has an "environment variables" section in the dashboard. Production secrets go there. They never live in a file that gets deployed. They are injected at build time or at process start.

The mental model I push founders toward: your repo contains the code and a `.env.example`

(no real values, just keys with empty strings). The hosting platform contains the real values. Local dev uses a `.env`

file that is in `.gitignore`

. Three places. No overlap.

A subtler version: do not screenshot your env vars dashboard, and do not paste that screenshot into Slack or Discord. I have seen Stripe keys leak through Loom recordings shared with a contractor.

Most providers let you create restricted keys. Stripe has restricted keys with read-only or specific-resource access. OpenAI has project-scoped keys. AWS has IAM roles with specific policies. The default behavior is to create a key with full account access because it is one click. Resist that.

A production API key should be able to do exactly what your app does and nothing more. If it is compromised, the blast radius is small. A founder I worked with last quarter had an AWS key with full admin access in a repo. The leak resulted in a small bitcoin-mining incident on her account that AWS reversed, but only after a week of stress. A scoped S3-only key would have made it "someone uploaded weird files to a bucket." Recoverable in 20 minutes.

For human access, use SSO and a password manager. Do not share login passwords with contractors. Use the vendor's "invite teammate" flow even if it costs a few euros a month. The audit trail alone is worth it.

Once a month, on the same day, you spend 5 minutes rotating one or two keys. Not all of them, just the highest-risk ones (the ones that can spend money). OpenAI, Anthropic, Stripe, your cloud provider. Over a year you rotate everything at least once.

This sounds like security theater. It is not. The point is to keep the rotation muscle warm so that when you actually need to rotate in a hurry (someone leaks a key on a Friday night), you already know which dashboard pages to open and what app code reads which env var. Without the muscle, you spend the first 45 minutes of an incident just orienting yourself.

I built HostingGuru in part because I kept solving this problem manually for clients. The platform has encrypted env vars by default on the Hobby tier (€19/month), restricted access logs that show which deploy injected which secret, AI monitoring that catches anomalous token usage patterns the moment a leaked OpenAI key starts being used by someone else, and Telegram alerts when a deploy starts behaving unlike its baseline. None of that is unique to HostingGuru. Most modern platforms have at least the env var encryption part. The piece I cared most about was making the secret-rotation flow take 30 seconds instead of 10 minutes, because friction is why founders skip rotation.

If you are happy on Vercel, Fly, Railway, Render, or anywhere else, this article still works. The point is the habit, not the host.

Five concrete moves you can make in the next hour.

First, install `gitleaks`

and run it against every repo in `~/code`

(or wherever you keep them). A one-liner for macOS:

```
brew install gitleaks
find ~/code -name ".git" -type d -maxdepth 3 \
  | while read d; do
      echo "=== $(dirname $d) ==="
      gitleaks detect --source "$(dirname $d)" --no-banner 2>&1 | tail -5
    done
```

Second, log in to GitHub and check the Secret scanning alerts tab on each of your repos. Anything sitting there has been visible to GitHub (and possibly to others) for as long as it has been there. Address it.

Third, rotate any key the audit found. Do not skip this. Revoke the old key explicitly in the provider's dashboard.

Fourth, install the `pre-commit`

hook with `gitleaks`

on your primary working repo. 90 seconds of setup, infinite future leak protection on that repo.

Fifth, put a recurring calendar event titled "Secret rotation, 5 minutes" on the first Monday of every month. When it fires, you rotate one provider key. Pick the highest-value one you have not rotated this year.

If you do these five things tonight, you have closed the biggest preventable security gap in your stack. The rest is just keeping the habit.

I have leaked a key in my own career. Twice. Once in 2017 when I committed an AWS key to a public-ish demo repo and got billed €80 of EC2 inside 6 hours. Once in 2022 when a Stripe test key ended up in a Loom recording sent to a client (test key, thankfully). Both times I learned the same lesson: the question is not "will a key leak" but "what is your detection and rotation time when it does." The audit and habits above bring that time from "weeks" to "minutes."

Here is the question I will leave you with. When was the last time you actually rotated a production API key, on purpose, without an incident forcing you to? If the answer is "never" or "I do not remember," your monthly calendar event starts tonight.

Drop a comment if you have a different rotation cadence that works for you, or if you have a horror story to share. I read all of them. We learn from each other's leaks more than from any best-practices doc.
