The most dangerous .env mistake is believing a deleted commit deletes a secret.
Everything else follows from four habits: commit .env.example, ignore .env, inject production values through a secret manager or the deployment environment, and rotate any key that enters Git history—even in a private repository.
.env
.env.*
!.env.example
| Safe habit | Why |
|---|---|
| Secret scanning in CI | Catches accidents early |
| Environment-scoped deploy secrets | Limits blast radius |
| Rotation on exposure | Makes old copies useless |
Masking a secret in CI logs is not access control. Do not pass credentials to untrusted pull-request code.
Treat secrets as revocable capabilities, not strings that happen to live in a file.
The path from accidental commit to incident
First rotate or revoke the credential. Then locate every environment that used it, review audit logs for suspicious use, and remove it from the working tree. Rewriting Git history can reduce future exposure, but it cannot retract clones, CI artefacts, fork caches, or a copied value in a ticket.
Keep CI boundaries narrow
Secrets should be attached only to protected deployment environments, never made available to arbitrary pull-request code. A workflow that checks out a contributor's branch and then injects production credentials has handed that contributor a secret execution environment. Use separate read-only test credentials for untrusted automation and require approval for deployment jobs.
dotenv remains useful for local parity. Its job is to describe configuration names, not to become the production vault.
Cover photo by Stanislav Kondratiev on Pexels.
