A while back I came across a .env file sitting wide open on a small app someone had built solo, mostly vibe-coded over a few weekends. Nothing clever happened to find it. No exploit chain, no bypassed login screen. Someone had just typed the domain followed by /.env, and the server handed the file back like it was a plain text page.
I want to walk through what was actually sitting inside that file, because I don't think most people who ship apps this fast really picture what they're handing over when this happens. That file rarely hands over just one key. It hands over the entire map of every service the app talks to, laid out in plain text.
What was actually in the file
Strip out the specifics and it read like almost every .env I've seen from an app like this: a database connection string, a payment processor's secret key, an email-sending API key, an API key for whichever AI provider the app called, a cloud storage access key and secret, a signing secret for the app's own login tokens, and an admin webhook URL. Eight lines. Eight different doors.
Each line opens a different room
- Database connection string. Direct access to every row in every table, no app logic and no permission checks standing between the reader and the data.
- Payment processor secret key. Enough to read customer records and transaction history, and on some setups issue refunds or trigger payouts.
- Email API key. Send mail as the app's own verified sending domain, which means it clears SPF and DKIM and lands in inboxes instead of spam folders.
- AI provider key. Run up usage on someone else's bill, and depending on how the app logs requests, pull user conversation data out of those logs.
- Cloud storage keys. Read or overwrite every file any user has ever uploaded.
- Token signing secret. Mint a valid session for any account, including an admin one, without ever touching a password.
Why this is worse than "a leaked key"
None of these need to be chained together to matter. Each line is a standalone credential that works the moment it's copied out. And before anyone even tries one, the variable names alone are reconnaissance: they tell an attacker exactly which vendors the app trusts, which gives them a shortlist of where to go next if any single key turns out to already be rotated.
Shipping fast and handling secrets carefully don't reliably show up in the same project, and a .env file is usually where that gap becomes visible first.
How the file gets out in the first place
The mechanics are almost always boring. A static host configured to serve dotfiles like any other request. A .env committed to git early on and never scrubbed from history, even after someone remembers to add it to .gitignore later. A public fork of what used to be a private repo. Or the file getting pasted into a support ticket or a shared doc while someone was debugging, and nobody thought to redact it before hitting send.
What actually changes the outcome
- Give each service its own scoped key instead of one root credential that can do everything. A leaked read-only key is a bad afternoon. A leaked key with full account access is a bad year.
- Keep secrets out of files where you can. Use your host's environment variable settings or a secret manager instead of a checked-in .env, even for local development.
- Treat any key touching billing, email sending, or admin auth as high blast radius, and rotate it on suspicion alone, not just confirmed proof of misuse.
- Check what a key can actually do before deciding how worried to be. A test-mode, read-only key is not the same emergency as a live secret key with payout access.
What stuck with me about that file wasn't any single credential on it. It was how ordinary the whole list looked. Nothing about it screamed sensitive. It read like a normal setup step, the kind of thing you paste in once during onboarding and never think about again. That's probably the actual problem: a .env file doesn't feel like a vault, so it rarely gets treated like one, even though it's holding a key to every room in the house.