Exposed .env file — the 30-minute incident response | Scanthra
May 2026https://yoursite.com/.env returns
your API keys, database password and SMTP credentials, treat it as an
active compromise even if nobody has used them yet. Rotate everything,
audit usage, then block the path. Plan on 30 minutes of focused work
and a few days of follow-up.
Why an exposed .env is serious
A .env file usually contains:
- Database username + password + host.
- API keys for Stripe, AWS, SendGrid, Mailgun, OpenAI…
- JWT signing secrets and OAuth client secrets.
- Sometimes admin credentials or "DEV password" used in seeding.
Internet scanners like
Shodan, BinaryEdge and dozens of credential-harvesting
botnets continuously fetch /.env on millions of hosts. If
yours is public, assume it's been read. Cost-of-rotation is much lower
than cost-of-breach.
Step 1 — Rotate every secret in the file
Open the file (or your backup of it) and for each entry:
- DB_PASSWORD — change in your database
(
ALTER USER … WITH PASSWORD …in PostgreSQL,ALTER USER … IDENTIFIED BY …in MySQL), then update your app config. - API keys — for each provider, go to dashboard → API keys → revoke the leaked key, generate a new one, update the config. Stripe, AWS and OpenAI all support live rotation without downtime.
- JWT / session secrets — rotate, knowing this will log out all users. Schedule for low-traffic window if needed but don't delay more than a few hours.
- SMTP / mailer credentials — rotate immediately; leaked SMTP credentials are the #1 spam vector.
Use a password manager or your secrets manager (AWS Secrets Manager, Doppler, 1Password, Bitwarden) to generate the new values. Never reuse any of the old secrets.
Step 2 — Audit usage for the leaked keys
Before you forget the old keys, check whether anyone used them while they were exposed:
- Stripe: Dashboard → Developers → Events. Filter by your old key. Look for charges, refunds, account changes you didn't make.
- AWS: CloudTrail event history filtered by the
leaked access key ID. Look for new IAM users, new EC2 instances,
S3 reads of unexpected buckets, anything
iam:*orec2:RunInstances. - SendGrid / Mailgun: activity feed for spikes, new template additions, IP changes.
- Database: audit logs (if enabled) for unexpected
queries; check
pg_stat_activityfor current sessions.
If you find anything suspicious: that's a confirmed incident — escalate, log, follow your IR plan, notify customers if personal data is involved. Under NIS2 in-scope entities must file an early warning within 24 hours.
Step 3 — Block the path permanently
The leaked file probably lived in your application root because the framework's webroot is one level too high. Two layers of defence are better than one:
Move the file out of webroot:
mkdir /etc/myapp
mv /var/www/myapp/.env /etc/myapp/.env
chmod 600 /etc/myapp/.env
chown www-data:www-data /etc/myapp/.env
Update your app to read from the new path.
And block dotfiles at the web server:
# nginx
location ~ /\.(?!well-known) { deny all; return 404; }
# Apache
<FilesMatch "^\.">
Require all denied
</FilesMatch>
Step 4 — Make sure it never happens again
- Use a secrets manager in production —
AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, Doppler.
The
.envfile becomes a developer convenience, not a deployment artefact. - Add a CI check that fails the build if
.envis committed.git secrets,trufflehogorgitleaksall do this. - Pin a pre-commit hook with
detect-secretsso leaks don't even reach main. - Rotate periodically anyway. Every 6 months for long-lived keys; immediately when a team member leaves.
What about Git history?
If .env was ever committed to a public repository — even
briefly, even years ago — assume the secrets are leaked. GitHub's
search and a thousand training-data scrapers have it. git filter-repo
rewrites history, but you still need to rotate the values, because
copies exist outside your control.
The boring lesson
Exposed .env is one of the most common Scanthra findings
across PHP, Laravel, Django, Rails and Node projects. The root cause
is almost always "I followed a tutorial that put .env in the project
root and forgot the webserver serves static files from there."
Moving the file one directory up — and adding the dotfile deny rule —
closes the entire class.
How Scanthra detects this
Our Hidden Files module checks a curated whitelist of paths
including .env, .env.local,
.env.production, .git/config,
/backup.sql, /dump.sql, /wp-config.php.bak
and more. We follow this up with magic-byte checks (ZIP, gzip,
SQLite headers) so we can tell a real backup from a 404 page that
happens to return 200.
Want to know if your site has this issue?
Scanthra runs a friendly, passive check and emails you a plain-English PDF report.
Scan your site free