Quick Answer
A .env file committed to Git exposes every secret it contains - database passwords, API keys, and auth tokens - to anyone with access to the repository. Deleting the file in a new commit is not enough; the secret remains in Git history. You must rotate every exposed credential, add .env to .gitignore, and optionally rewrite history to fully clean the repository.
How Common Is This, and Why Does It Happen with AI Tools?
Committing a .env file is the single most common security mistake in vibe-coded projects. When you use Bolt, Lovable, Cursor, or v0 to scaffold a new app, the AI generates a working project structure as fast as possible. That often means creating a .env file with real values, running git init, and pushing everything to GitHub - without ever pausing to configure .gitignore first.
According to GitGuardian's 2024 State of Secrets Sprawl report, over 12.8 million secrets were detected in public GitHub repositories in a single year - a 28% increase year-over-year. Environment files are the most common vehicle for those leaks. GitHub's own data shows that secrets are typically accessed by automated scanners within minutes of being pushed to a public repository.
The problem is compounded by the fact that many AI tools generate .env files with placeholder values that developers replace with real credentials and then commit alongside the rest of their work. What started as a template instantly becomes a live security incident the moment real values are added and pushed.
Even private repositories are not safe. OWASP's security guidance notes that repositories change visibility over time, team members with access can change, and forks of private repos may become public. Once a secret is in Git history, the surface area of exposure grows with every day it remains unrotated.
What Gets Exposed When a .env File Is Committed
A typical .env file in a vibe-coded app contains a collection of secrets that, individually, each represent a significant security risk:
# ❌ BAD - This entire file should NEVER be committed to Git
DATABASE_URL=postgresql://postgres:[email protected]:5432/mydb
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIn0.XXXX
OPENAI_API_KEY=sk-proj-abc123def456ghi789jkl012mno345pqr678stu
STRIPE_SECRET_KEY=sk_live_51ABC123DEF456GHI789JKL012MNO345PQR678
NEXTAUTH_SECRET=my-super-secret-nextauth-key-that-should-be-random
SENDGRID_API_KEY=SG.abc123def456ghi789jkl012mno345pqr678stu901vwx
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Each of these is a distinct liability. An exposed STRIPE_SECRET_KEY allows attackers to process charges, create refunds, and read customer data. An exposed SUPABASE_SERVICE_ROLE_KEY bypasses Row Level Security entirely and gives full read/write access to your database. An exposed AWS_ACCESS_KEY_ID with a secret key can provision infrastructure and run up thousands of dollars in cloud bills within hours.
Why Deleting the File Is Not Enough
This is the part that catches most developers off guard. Git is an append-only version control system - every change you make is stored in history as a new commit. When you delete .env and push a new commit, the current state of the repository no longer contains the file, but every previous commit that included it still does.
Anyone can retrieve your secrets by running:
# Any of these commands retrieves secrets from Git history
git log --all --full-history -- .env
git show HEAD~1:.env
git log -p | grep -A 5 "STRIPE_SECRET_KEY"
This means that the moment a .env file with real credentials is committed and pushed - even for one second - those credentials must be treated as compromised. The only correct response is to rotate them immediately in the provider's dashboard, not to simply remove the file.
How to Detect a Committed .env File
| Detection Method | What It Finds | Limitation |
|---|---|---|
| Check current repo state | git ls-files | grep .env |
Only shows current working tree, not history |
| Search full Git history | git log --all -- "**/.env" |
Manual, easy to miss patterns |
| Gitleaks scan | All secret patterns in history | Requires installation |
| GitHub Secret Scanning | Known token patterns in public repos | Public repos only on free plans |
| VibeDoctor automated scan | PRJ-001, PRJ-002 checks + full history | Requires repo access grant |
How to Fix a Committed .env File
Follow these steps in order. Skipping step one means the rest is security theater:
Step 1 - Rotate every exposed credential immediately. Go to each provider's dashboard (Stripe, Supabase, OpenAI, AWS, etc.) and generate a new key. Revoke the old one. Do this before anything else.
Step 2 - Add .env to .gitignore. Create or update your .gitignore file at the project root:
# ✅ GOOD - Add these lines to your .gitignore
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.env.*.local
Step 3 - Remove the file from tracking. Even after adding to .gitignore, if the file is already tracked by Git, you must explicitly stop tracking it:
# ✅ GOOD - Remove from tracking without deleting the local file
git rm --cached .env
git commit -m "Remove .env from tracking"
Step 4 - Create a .env.example file. This committed placeholder tells other developers which variables they need, without exposing actual values:
# ✅ GOOD - .env.example (safe to commit)
DATABASE_URL=postgresql://user:password@host:5432/dbname
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key-here
OPENAI_API_KEY=sk-your-key-here
STRIPE_SECRET_KEY=sk_live_your-key-here
NEXTAUTH_SECRET=generate-with-openssl-rand-base64-32
Step 5 - Optionally rewrite history. If your repository is public, or if you cannot guarantee the old keys have never been accessed, use git filter-repo to remove the file from all commits. This is a destructive operation that rewrites history - coordinate with your team before running it on a shared repository.
How to Scan for This Issue Automatically
Manual inspection is unreliable, especially on larger projects where dozens of AI-generated files may contain secrets in unexpected places. Tools like VibeDoctor (vibedoctor.io) automatically scan your codebase for committed .env files and related secret exposure issues, flagging specific file paths and line numbers. Free to sign up.
For ongoing prevention, set up a pre-commit hook using Gitleaks. This runs a secret scan before every commit and blocks the push if secrets are detected:
# Install Gitleaks as a pre-commit hook
brew install gitleaks # macOS
# or download from github.com/gitleaks/gitleaks
# Run a one-time scan of your current repo
gitleaks detect --source . -v
# Scan full Git history (finds secrets in old commits)
gitleaks detect --source . --log-opts="--all" -v
Vercel, Netlify, Railway, and Render all provide environment variable storage in their dashboards. For production deployments, never rely on .env files - use the platform's secret management system and reference variables via process.env.VARIABLE_NAME in your code.
What a Secure Project Structure Looks Like
A Next.js project built with Cursor or Lovable should have the following environment file setup from the start:
# Files in your repository root
.env.example # ✅ Committed - placeholder values only
.env # ❌ Never committed - real values, in .gitignore
.gitignore # ✅ Committed - includes .env pattern
# Vercel / production
# All variables set via: vercel env add VARIABLE_NAME production
When another developer clones the project, they run cp .env.example .env and fill in their own values. No secrets ever travel through Git.
FAQ
I deleted the .env file in a new commit - are my secrets safe now?
No. The secrets are still visible in Git history to anyone who runs git log -p. You must rotate every credential that was in the file, regardless of whether you removed the file. Treat those credentials as permanently compromised from the moment they were pushed.
My repository is private - does this still matter?
Yes. Private repositories can be forked, made public accidentally, accessed by compromised team member accounts, or transferred in ways that change visibility. Any secret in Git history is a liability. Additionally, if your company is ever acquired or audited, historical commits are reviewed.
Does Bolt or Lovable create .gitignore files automatically?
Sometimes, but not reliably. AI tools vary in whether they generate a proper .gitignore and whether it includes the full range of .env patterns. Always verify that .env is explicitly listed before your first git push. Run git status and confirm the file is not shown as tracked.
What is the .env.example file for?
It's a committed template that lists every environment variable your project needs, with placeholder values instead of real secrets. New developers copy it to .env and fill in their own credentials. It serves as living documentation of required configuration without exposing anything sensitive.
How do I set environment variables on Vercel instead of using .env?
In your Vercel dashboard, go to your project settings and select the Environment Variables tab. Add each variable for the Production, Preview, and Development environments as needed. The Vercel CLI also lets you run vercel env add VARIABLE_NAME from the terminal. Your deployed app accesses them via process.env.VARIABLE_NAME exactly as it would locally.