RoastMyOpsec

Vulnerabilities

How to Check If Your .env File Is Exposed (and What to Do If It Is)

October 23, 2026

Short answer: request https://yourdomain.com/.env with a GET and read the body, not just the status code. If what comes back looks like KEY=value lines (DATABASE_URL=, STRIPE_SECRET_KEY=, AWS_SECRET_ACCESS_KEY=), it's exposed. Treat every value in it as stolen. Rotate first, block the path second, and check everywhere else third. A 404 or 403 is what you want to see. A 200 with your homepage HTML is a soft 404, not a pass and not a leak. Only test domains you own or have permission to test.

Your .env file was supposed to live a quiet life on a server. It was never meant to sit next to index.html like a welcome mat that reads "database password inside." Here's how to find out whether yours is doing that.

Step 1: Ask for the file the way a bot would

Scanners crawling the internet don't send a polite email first. They request well-known paths and read whatever comes back. Do the same thing to your own site:

curl -s -D - https://yourdomain.com/.env | head -n 30

-D - prints the response headers and head keeps a real leak from flooding your terminal. You're looking at three things:

What you seeWhat it meansWhat to do
404 or 403, small generic bodyNot served, or deniedGood. Still add an explicit deny (Step 4)
200, text/html, body is your app/homepageSoft 404: your SPA or catch-all route answeredNot a leak, but not a lock either. Add the deny
200, body is KEY=value linesExposedStop reading and go to Step 3
301/302RedirectFollow it with -L and check where it lands

Why the status code alone lies: plenty of single-page apps and catch-all routes return 200 for every path, so /.env "succeeds" and hands back your marketing page. A checker that only reads status codes will either panic over nothing or wave through a real leak. Read the body. Check the Content-Type. Our soft 404 guide goes deeper.

Use GET, not just HEAD. curl -I sends a HEAD request, and some stacks route HEAD differently from GET. The bots use GET, so you should too.

Step 2: Check the cousins, and every hostname

.env rarely leaks alone. Run the same check against the usual suspects:

for p in .env .env.local .env.production .env.development .env.backup .env.bak .git/config .git/HEAD; do
  printf '%-20s ' "/$p"
  curl -s -o /tmp/probe -w '%{http_code} %{content_type}\n' "https://yourdomain.com/$p"
  head -c 120 /tmp/probe | tr '\n' ' '; echo; echo
done

Then repeat it for every hostname you actually serve: apex and www, staging., dev., old marketing subdomains, and preview deployments. The hardened production site is rarely where this goes wrong. The forgotten staging box that someone deployed with scp -r . is.

A note on .env.example: it should be public in your repo with empty or fake values. It should not be served from your web root, and it should never hold a real key "just for now."

Step 3: If it's exposed, rotate first

Here's the hard part: blocking the path doesn't un-leak the file. If /.env returned real values, assume someone already has them. Bots request this path all day, every day.

In this order:

  1. Rotate every secret in the file. Database passwords, API keys, JWT/session signing secrets, SMTP credentials, payment keys, cloud access keys. If it's in the file, it gets a new value.
  2. Revoke the old ones, not just "add new ones." Old keys that still work are still keys.
  3. Check access logs for successful (2xx) requests to /.env and friends. That tells you when it started and how many times it was fetched.
  4. Check the blast radius. Did anyone use those cloud keys, sign tokens, or log into that database since then? Look at the provider's audit logs.
  5. Then remove the file and block the path (Step 4), and redeploy.

If .git/ was exposed too, your source and commit history may be recoverable. That includes secrets you "deleted" three commits ago. Rotate those as well. More in exposed .git vs .env.

Step 4: Make sure it can't happen again

Deny dotfiles at the edge or origin. Don't rely on the file just not being there. Nginx:

# Block dotfiles but keep /.well-known/ (ACME, security.txt) working
location ~ /\.(?!well-known) {
    deny all;
}

Apache 2.4:

<FilesMatch "^\.">
    Require all denied
</FilesMatch>

On static hosts and CDNs, the fix is usually upstream: make sure .env* and .git never end up in the published output directory in the first place.

Keep secrets out of the build artifact. Use your host's environment variable settings or a secrets manager. If there's no .env file in the deployed folder, there's nothing to serve.

Fail CI if it shows up. One line in your pipeline that checks the build output for .env* or .git and fails the deploy is cheaper than one incident.

Don't "hide" it in robots.txt. Disallow: /.env doesn't block anyone. It tells everyone exactly where to look.

The sneakier leak: secrets in your JavaScript

Your .env can be locked down and still leak through the front door. Frameworks inline some environment variables into the client bundle on purpose: NEXT_PUBLIC_* in Next.js, VITE_* in Vite. Anything with those prefixes ships to every visitor's browser. That's fine for a public analytics ID and a disaster for a secret key. Search your built JS for key-shaped strings, and read public JS API keys vs server secrets.

Get roasted before the bots get there

Running curl loops across every hostname gets old fast. RoastMyOpsec's free audit does this check on a URL you own. It sends bounded GETs to well-known sensitive paths like .env, .git, backups, and package manifests. It checks for soft 404s and real file signatures, so a catch-all homepage doesn't fool it. It also scans your same-origin JavaScript for high-confidence secret patterns. No exploit payloads and no card. You get a score from 1 to 10 and five findings for free, with deeper findings in the vault if you want them.

A clean result means those well-known paths didn't look like real files. It doesn't mean every hostname you've ever deployed is clean, so keep the deny rules either way.

Run the free self-audit →

FAQ

Does a 200 on /.env always mean it leaked?
No. On many SPAs it's your homepage HTML. Read the body and the Content-Type.
Is a 403 safe?
It means the path is denied, which is what you want. Make sure it's denied on every hostname, not only the main one.
I removed the file. Am I done?
Not if it was ever served. Rotate everything that was in it.
Can I check someone else's site?
Don't. Test only what you own or have written permission to test.

Related guides