EMZETT.
Login

.env Files

In short: Text files in which configuration values and secrets (API keys, database credentials) are stored as key-value pairs, instead of being hard-coded in the source code.

In more detail: A .env file contains lines in the format KEY=value and is read when an application starts, so that the code can access them via environment variables. The central security reason for this: secrets don’t end up in the source code and therefore not in version control (Git) — .env files basically belong in .gitignore. There are often several variants for different environments, e.g. `.env.local` for local development.

In Depth

A typical .env file looks like this:

DATABASE_URL=postgresql://user:pass@host:5432/dbname
STRIPE_SECRET_KEY=sk_live_...
SESSION_SECRET=a1b2c3...
NODE_ENV=production

At start-up, a package (in Node.js, e.g. dotenv) reads this file and makes each line available as an environment variable in the process (process.env.DATABASE_URL). Important: on modern deployment platforms (e.g. Vercel, Docker), production secrets are usually NOT set via a .env file in the file system, but directly via a secured web interface or the platform’s configuration — .env files are primarily a tool for local development.

The most common security mistake with .env files: accidentally committing them after all, because .gitignore was only added AFTER the first git add — once in the Git history, the secrets remain visible there even after being deleted later, unless the history is actively cleaned. The standard approach is therefore: .env in .gitignore from the start, plus a .env.example file with the same key names but placeholder values, so that other developers know which variables are needed without seeing the real secrets.

What to do if a secret was committed after all

If a real secret ends up in the repository anyway (even in just a single historical commit), it’s NOT enough to simply delete the file in the next commit — the history remains visible to anyone with repo access (git log -p also shows lines long since deleted from old commits). The correct first step is therefore always to revoke the compromised secret IMMEDIATELY with the respective provider and generate a new one — cleaning the Git history (e.g. with git filter-repo) is sensible, but doesn’t retroactively make an already compromised secret safe once it was publicly visible (especially in public GitHub repositories, which are constantly searched by automated bots for exactly such patterns).

Automated secret detection

Because this mistake is so common, many platforms offer automated protective mechanisms: GitHub secret scanning automatically searches public (and, with a suitable plan, also private) repositories for known secret patterns (e.g. the characteristic prefix of a Stripe or AWS key) and notifies both the provider and the repository owner, often before a human even notices the mistake. Local pre-commit hooks (e.g. git-secrets, gitleaks) can fulfil the same purpose even BEFORE the push, by blocking a commit that contains suspicious patterns.

Difference from real secret management systems

For production environments with high security requirements, simple .env files are often not enough — dedicated secret management systems (e.g. HashiCorp Vault, AWS Secrets Manager) offer additional functions such as automatic rotation (secrets are replaced automatically at regular intervals, without manual intervention), fine-grained access control (which service may read which secret) and complete audit logs (who accessed which secret and when). For smaller projects and local development, the simple .env file nevertheless remains the pragmatic standard.

See also: .env.local, Environment Variables