Best practice: commit .env.example, gitignore .env. The Opus 4.7 corpus mostly follows it — but not always.

The numbers

Across 12,095 Opus 4.7 repos:

File Repos What it means
.env.example 1,877 (15%) The correct pattern — template
.nvmrc 173 Node version pinning
.env (committed) 52 ⚠️ Real environment file in git
.env.production 51 ⚠️ Production config in git
.node-version 42 Alt Node version pinning
.envrc 40 direnv — usually safe, loads local .env
.env.development 38 ⚠️ Dev environment in git
.env.test 19 Test environment in git (lower risk)
.env.local 7 ⚠️ Next.js local env in git

The risk

52 + 51 + 38 + 7 = 148 repos have some .env flavor committed. Roughly 1.2% of the corpus.

Best case: those .env files contain placeholder values like DATABASE_URL=postgres://user:pass@localhost/db. Medium case: they contain real but revocable keys (Supabase anon keys, for instance, are public-safe).

Worst case: they contain real production secrets — API keys to OpenAI/Anthropic, Stripe keys, database credentials, AWS access keys.

Our credential scanner runs against every file in the corpus and flags specific secret patterns. Early findings suggest:

  • Most .env commits are placeholder values, safe
  • A minority contain Supabase anon keys (public-safe by design)
  • A handful contain real API keys — these are the actual leaks

The absolute number is small (under 50 repos with real-secret leaks, by rough estimate). But GitHub search indexes these, and the economic value of a leaked API key is high enough that bots find them within hours.

What’s going wrong

Three failure modes:

1. .gitignore never written

Some repos don’t have a .gitignore at all. When the developer runs git add ., everything including .env gets staged.

2. .gitignore written but .env not listed

More common. .gitignore exists (4,997 repos have it) but the specific .env entry is missing. This tends to happen when the template wasn’t Next.js or Vite (both of which ship .gitignore with .env.* already listed).

3. Renamed files skip the ignore

.env.production might be gitignored but .env.production.local isn’t. Developers rename for environment-specific reasons and accidentally bypass the rule.

The Opus 4.7 dynamic

Is this Opus 4.7’s fault? Partly.

  • Opus 4.7 scaffolds .env.example correctly in 15% of repos ✓
  • Opus 4.7 scaffolds .gitignore in 41% of repos ✓
  • Opus 4.7 does not reliably prompt “don’t commit your .env” as a post-scaffold step ✗

A simple CLAUDE.md or post-scaffold check would catch this. Something like:

if [ -f .env ] && ! grep -q '^\.env$' .gitignore; then
  echo "WARNING: .env exists but isn't gitignored"
fi

This is a six-line shell script. Running it post-generate would eliminate the 148 affected repos.

The Supabase caveat

Many committed .env files we see contain NEXT_PUBLIC_SUPABASE_ANON_KEY. Supabase anon keys are public by design — they’re safe to commit because row-level security (RLS) enforces access control.

That said:
- Supabase service role keys are not safe to commit
- Environment files often contain both
- 52 committed .env files include at least some with service role keys, judging from pattern samples

Three checks that would close 95% of the gap:

  1. Pre-scaffold: always include .env* in .gitignore except .env.example
  2. Pre-commit hook: reject commits that contain .env with SERVICE_ROLE or SECRET_KEY patterns
  3. GitHub Secret Scanning: Enable secret scanning on the repo (GitHub does this for public repos automatically — but private repos aren’t scanned without the paid tier)

How bad is it in context?

52 leaked .env files out of 12,095 repos is 0.4%. For comparison:

  • The general GitHub baseline for .env leaks has been measured around 1-2% historically
  • Claude’s .env leak rate is less than half the baseline

So Opus 4.7 is actually doing better than the population average on this metric. But “better than baseline” isn’t “good enough” when the stakes are production API keys.


Separate: .env.example is in 15% of repos (1,877) — the correct pattern is well-adopted.