.env Files Security Best Practices: Stop Leaking Secrets

Published March 9, 2026 · SPUNK LLC · 12 min read

The .env file is the most convenient and most dangerous file in any project. It holds database URLs, API keys, signing secrets, and authentication tokens in plaintext. One accidental git add . and every secret in that file is permanently embedded in your repository history, even if you delete it in the next commit.

GitHub's secret scanning service detects over 200 types of leaked credentials and reports that millions of secrets are exposed in public repositories every year. The majority originate from committed .env files. This guide covers every practical step to keep your dotenv files from becoming your biggest liability.

The Fundamentals: .gitignore Is Not Enough

Every developer knows to add .env to .gitignore. But this single line of defense fails in several predictable ways:

A comprehensive .gitignore pattern

# Block all .env variants
.env
.env.*
!.env.example
*.env
env.local
.secrets
credentials.json

The !.env.example exclusion is intentional. Your example file with placeholder values should be committed to document what variables the project requires.

Pre-Commit Hooks: The Safety Net

Relying on developers to never commit a .env file is relying on human perfection. Pre-commit hooks automate the check and block the commit before it happens.

Using git-secrets

AWS's git-secrets tool scans staged changes for patterns that look like secrets (AWS access keys, private keys, tokens). Install it globally and it runs automatically on every commit:

# Install
brew install git-secrets

# Register AWS patterns
git secrets --register-aws --global

# Add custom patterns for your keys
git secrets --add --global 'sk_live_[a-zA-Z0-9]{24}'
git secrets --add --global 'AKIA[0-9A-Z]{16}'

# Install the hook in your repo
git secrets --install

Using detect-secrets

Yelp's detect-secrets takes a different approach. It generates a baseline file listing known secrets (by hash, not value) and alerts you when new secrets appear in commits. This avoids false positives from existing, intentionally committed values:

# Create a baseline
detect-secrets scan > .secrets.baseline

# Add the pre-commit hook
detect-secrets-hook --baseline .secrets.baseline

Using Husky with lint-staged

For JavaScript projects, combine Husky and lint-staged to reject commits that include .env files:

// package.json
{
  "lint-staged": {
    ".env*": ["echo 'ERROR: Attempting to commit .env file' && exit 1"]
  }
}

What Goes in .env vs. What Does Not

Not everything belongs in a .env file, even if it is technically configuration:

Belongs in .env

Does not belong in .env

Encryption At Rest

Storing secrets in plaintext .env files on your local machine means anyone with file system access (malware, a stolen laptop, a compromised backup) has your keys. Encrypting your .env files adds a layer of protection.

SOPS (Secrets OPerationS)

Mozilla's SOPS encrypts files while preserving their structure. You can encrypt a .env file using a GPG key, an AWS KMS key, or age. The encrypted file is safe to commit because it cannot be decrypted without the key:

# Encrypt with age
sops --encrypt --age $(cat ~/.config/sops/age/keys.txt | grep "public" | cut -d: -f2 | tr -d ' ') .env > .env.enc

# Decrypt when needed
sops --decrypt .env.enc > .env

dotenvx

The dotenvx tool encrypts .env files natively and decrypts them at runtime. You commit the encrypted .env.vault file and store the decryption key as a single environment variable in your deployment platform:

# Encrypt your .env file
dotenvx encrypt

# Run your app with automatic decryption
dotenvx run -- node app.js

What to Do When You Accidentally Commit a .env File

If you committed a .env file, deleting it and committing the deletion is not sufficient. The secrets are still in your git history. Here is the complete remediation process:

  1. Rotate every secret immediately. This is the most important step. Do not spend time cleaning git history before rotating. Assume the secrets are compromised from the moment they were pushed.
  2. Remove from git history. Use git filter-repo (preferred over the deprecated git filter-branch) or BFG Repo-Cleaner to rewrite history and remove the file from all commits.
  3. Force push the cleaned history. This requires all collaborators to re-clone or reset their local copies.
  4. Invalidate GitHub caches. Contact GitHub support to clear cached views of the commit containing the secret. The commit may be visible in pull request diffs even after history rewriting.
  5. Audit for unauthorized usage. Check the access logs and usage dashboards for every service whose key was exposed. Look for unusual activity between the commit time and the rotation.

Prevention is always cheaper than remediation. A single committed .env file can take hours to fully remediate across git history, CI caches, deployment artifacts, and service audits.

Docker and .env Files

Docker Compose reads .env files natively, which creates additional exposure points:

# docker-compose.yml (safe to commit)
services:
  api:
    build: .
    env_file:
      - .env  # NOT committed

Runtime Security

Even if your .env file is properly secured at rest, secrets can leak at runtime:

The Modern Alternative: No .env Files at All

The most secure .env file is one that does not exist. Modern secret managers eliminate local files entirely:

The shift from file-based secrets to managed secrets is the single biggest improvement most teams can make to their secret hygiene. Files are leakable. Managed secrets with access controls and audit logs are not.

Checklist

  1. Comprehensive .gitignore patterns covering all .env variants
  2. Pre-commit hooks that scan for secrets before every commit
  3. Encrypted .env files if you must use files at all
  4. Separate .env files per environment (dev, staging, production)
  5. No secrets in Dockerfiles, build args, or image layers
  6. Runtime scrubbing in error reporters and loggers
  7. A tested remediation plan for accidental commits
  8. Migration path toward managed secret infrastructure

Your .env file is the single most sensitive file in your repository. Treat it that way, or accept the statistical certainty that its contents will eventually leak.

Recommended Security Tools

Go beyond dotenv best practices with these tools: