Exposed .env File: What It Leaks and How to Check
A publicly accessible .env file hands attackers database passwords, API keys, and secret tokens in plaintext. Here's what's at risk and how to detect it.
In October 2022, security researcher Bob Diachenko found over 900,000 publicly accessible .env files indexed by Shodan and similar tools. Each one was a complete credential dump — database connection strings, AWS access keys, Stripe secrets, mail server passwords. The .env file is the most dangerous accidental disclosure in modern web development because it is designed to contain every secret the application needs to run.
What a .env file contains
The .env convention (popularized by the Twelve-Factor App methodology) stores environment-specific configuration outside of source code. In practice, a typical Laravel, Django, Next.js, or Rails application's .env includes:
- Database credentials:
DB_PASSWORD, connection strings with embedded passwords - Session and encryption keys:
APP_KEY,SECRET_KEY_BASE,JWT_SECRET - Third-party API keys: Stripe, Twilio, SendGrid, Mailgun, Slack, GitHub tokens
- Cloud provider credentials:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, GCP service account details - OAuth secrets: client IDs and secrets for Google, Facebook, GitHub login
- Internal service URLs: internal API endpoints, Redis/Memcached connection strings
- SMTP credentials: mail server usernames and passwords
A single exposed .env file can give an attacker full database access, the ability to forge authentication tokens, direct API access to payment processors, and cloud console credentials that control your entire infrastructure.
How .env files get exposed
The root cause is almost always the same: the file is placed in the web root (or a subdirectory of it) and the web server is configured to serve static files without a whitelist. Common scenarios:
- Shared hosting deployments where the document root is
public_html/but developers deploy the entire project there, placing.envatpublic_html/.env - PHP frameworks misconfigured — Laravel's document root should be
public/, not the project root; placing it wrong puts.envatyourdomain.com/.env - Docker deployments that COPY the entire build context including
.envinto the container's web-served directory - CI/CD artifacts — build pipelines that output
.envinto a directory that later gets synced to an S3 bucket with public-read ACL - Git-deployed sites — if a deploy script runs
git checkoutinto a document root, any file not in.gitignorebecomes publicly served
Detection: checking if your .env is exposed
The check is a single HTTP request:
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.envA 200 response is a confirmed exposure. Download the file and you'll see your secrets in plaintext. Check additional variants:
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.env.local
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.env.production
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/.env.backup
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/env
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/env.txtDevelopers often create .env.backup or .env.old when rotating credentials — those are equally dangerous. Also check subdirectories if your application has multiple services:
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/api/.env
curl -s -o /dev/null -w "%{http_code}" https://yourdomain.com/backend/.envChecking for historical exposure
Even if you've fixed the exposure, you need to check whether the file was crawled before you fixed it. The Wayback Machine (archive.org) indexes publicly accessible files:
curl -s "https://archive.org/wayback/available?url=yourdomain.com/.env"If the response includes a snapshot URL, your .env was publicly accessible at that point and may have been saved. Assume any secret in that file is compromised.
Immediate response if exposed
If your .env is or was exposed, treat every credential in it as compromised:
- Rotate the database password immediately — change it at the database server, update the application config, restart
- Regenerate the application secret key (this invalidates existing sessions)
- Revoke and regenerate all third-party API keys (Stripe, SendGrid, AWS, etc.) — do this through each provider's console, not just by changing the
.env - Check AWS CloudTrail / GCP audit logs for any API calls made with the exposed credentials
- Audit your database for unexpected queries or data access in the hours/days the file was exposed
- Check for new IAM users, access keys, or role policy changes if AWS credentials were exposed
Remediation: blocking .env from web access
Nginx
# Block dotfiles entirely
location ~ /\. {
deny all;
return 404;
}
# Or specifically
location = /.env {
deny all;
return 404;
}Apache
Require all denied
This blocks all dotfiles — .env, .htaccess backups, .git/config, and any other hidden files.
Correct the document root
The proper fix is structural. For Laravel, the document root must be /var/www/your-app/public, not /var/www/your-app. The .env file lives in the project root, one level above what the web server serves. The same principle applies to Symfony (public/), Next.js (.next/static is served, not the repo root), and other frameworks.
Remove .env from Docker images
# In .dockerignore
.env
.env.*
!.env.exampleUse Docker secrets or environment variables injected at runtime rather than baking the .env file into the image layer.
Common mistakes
- Blocking
/.envbut not/.env.localor/.env.production— use a regex or wildcard rule that matches all dotenv variants - Rotating secrets but not revoking old ones — creating a new AWS access key doesn't invalidate the old one; you must explicitly delete the old key
- Checking only the root domain —
api.yourdomain.com/.envandstaging.yourdomain.com/.envare separate attack surfaces - Relying on obscure filenames —
.env_prodorconfig.envwill still appear in automated scans if they're web-accessible - Committing .env to a public repository — even if removed in a later commit, git history retains the file; use
git filter-branchor BFG Repo Cleaner to purge it, and rotate all credentials
Orb44 scans for exposed .env files and their common variants on every scan, including on subdomains. If a previously clean domain starts returning 200 for /.env — after a deployment, for instance — you'll get an alert before automated scanners index it.
FAQ: My .env returns 403, is that safe?
A 403 means the server knows the file exists and is denying access. That's better than 200, but the file is still in the web root. A web server misconfiguration, a secondary vhost, or a path traversal vulnerability could still expose it. The correct fix is to move the file outside the document root entirely, so there's nothing to block.
FAQ: Should I use a secrets manager instead?
For production deployments, yes. AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager, and similar tools avoid the problem of plaintext secrets in files. They add operational complexity, but they also provide audit logs, automatic rotation, and fine-grained access control. The .env file pattern is pragmatic for development; production environments should use native secret injection where possible.
FAQ: How do attackers find .env files at scale?
Several methods run continuously: Shodan and Censys index HTTP response bodies and can search for patterns matching env file contents; automated bots probe every discovered web server for a fixed list of high-value paths; Google dorks like site:yourdomain.com filetype:env occasionally surface indexed env files. Exposure windows as short as minutes have been exploited in documented incidents.