Check whether a site's .env file is exposed
The .env file holds the passwords and keys a site runs on. When a web server hands it out by mistake, anyone who knows the address can read the whole thing. It is one of the most common leaks, and one of the worst. This page explains how it happens and offers a free check that takes a few seconds.
What a .env file is, and why it matters
A .env file is a plain list of KEY=VALUE lines that an application reads on start-up. It exists precisely so that credentials stay out of the source code: the database password, the payment provider's secret key, the mail credentials, the session signing secret. Everything the application needs to be itself, in one file, in the clear. That design is sound as long as one assumption holds, which is that only the server ever reads it.
When the file is served instead, the loss is not partial. There is no such thing as reading half of a .env: whoever requests the path gets every line at once, and each line is a working credential rather than a hint. A leaked database password is direct access to the data; a leaked payment key is charges made in the site's name; a leaked mail credential is mail sent from the domain. The application itself is untouched and keeps working normally, which is why this can run for months without anyone noticing.
It is also cheap to find. Nobody chooses a site for this. Automated sweeps request /.env against every address they can reach, and the cost of asking is one HTTP request, so they ask everywhere. Being small, new or uninteresting offers no protection at all.
Sites assembled with an AI coding tool hit this more often than any other group, because the working directory is what gets deployed. What AI-built sites tend to leak covers that case specifically.
How it ends up public
Nothing about a served .env file looks like a mistake from the inside. The site works, no error is raised and no page changes, so the leak is invisible until somebody looks at the site from the outside. There are three routes to it, and they are not mutually exclusive.
The first is the deploy. A .env sits at the top of the project folder, next to the code, and when the whole folder is copied to the web root instead of a build artefact, the file goes with it. Uploading over FTP, cloning a repository into the document root, or copying a working directory to the server all produce this. The file is then a normal file inside the published tree, and the web server serves it as text, because that is what it does with files it has no special rule for.
The second is the repository. A .env committed once is in the history permanently, so it travels to every clone, every fork and every mirror of that repository, and it survives a later commit that deletes it. If the repository is public, or becomes public, the file is public with it. This route leaks the credential even when the web server is configured perfectly.
The third is the server itself. Some stacks block dot-files by default and some do not, and a rule that exists on the origin does nothing for a CDN or reverse proxy that answers first. Variants are the usual failure here: a configuration that denies /.env will happily serve /.env.local, /.env.production, /.env.bak or /.env~, which are the same secrets under a different name.
How it looks in the report
A Deep Audit reports this as a Critical finding titled "Environment file is publicly readable", naming the path that answered and the signal that identified it. The signal matters: the verdict is not "this path returned 200", which a custom error page also does, but "this response was a KEY=VALUE environment document rather than an HTML page", established from the response body itself. The audit reads enough bytes to be sure, describes the file in format terms, and drops them. A leaked key is reported as leaked; it is never copied into a database to prove the point.
The check is part of the active layer, which runs after domain ownership is verified. Requesting a path nothing links to is probing rather than reading, and CheckWeb does not do that to a domain on a stranger's behalf. So the free passive scan never reports this, and a clean passive report is not evidence that a .env file is absent.
Environment file is publicly readable
An environment file is being served. This is where database passwords, API keys and app secrets live, and anyone who requests this path gets the file.
/.env
Exposed vs closed
Exposed
A request for /.env returns lines such as DB_PASSWORD=… and STRIPE_SECRET_KEY=… as plain text. The same is true of /.env.production and /.env.bak, which are frequently open on a site whose /.env is blocked.
Closed
Every one of those paths returns 404 or 403, the file lives outside the document root, and the values the application actually uses come from the host's environment rather than from a file inside the published tree.
Closing the path stops new copies being taken. It does nothing about the copies already taken, which is why rotation is the first step below and not the last.
If it is exposed, what to do
- Rotate first, before anything else. Change the database password, issue new API keys and tokens, and regenerate any application secret or salt the file held. Assume every value in it is public: for as long as the path answered, it was. Then check each provider's usage and billing pages for activity nobody recognises.
- Move the file out of the document root, so there is no path to request. A file the web server cannot reach cannot be misconfigured into being served.
- Block dot-files at the web server as a second layer. Nginx: `location ~ /\. { deny all; return 404; }`. Apache: `RedirectMatch 404 /\..*$`. Both cover .env, .git and .aws in one rule.
- Add the same rule at the CDN or reverse proxy. An origin rule does nothing about a path the edge already answers or has cached.
- Cover the variants explicitly: .env.local, .env.production, .env.staging, .env.bak, .env.old and .env~ are the same secrets under names a narrow rule misses.
- Restart the application so it picks up the rotated values, then confirm the site still works. A rotation that is never loaded looks like a fix and is not one.
- Change what gets deployed: publish build output rather than the project directory, and keep secrets in the host's environment variables instead of a file inside the published tree. This is the step that stops the next one happening.
- Then verify by asking again, from outside the network, and confirm the finding is gone. (A re-run of the audit does this and records the result; the wider secrets catalogue covers what else is worth asking for at the same time.)
Run a free check
The free passive scan reads headers, TLS and known CVEs on any site in about thirty seconds, with no signup. Verifying that a domain is owned adds the active layer, which is where the environment files, backups and repository folders are actually requested by name.
Check my websiteFAQ
- Is deleting the file enough?
- No. Deleting it stops further downloads, which is worth doing immediately, but it does nothing about copies already taken, and there is usually no way to tell whether any were. Every credential the file contained has to be rotated before the incident is closed.
- The .env file is blocked, but is .env.production also blocked?
- Frequently not, and that is the most common partial fix in this whole category. Rules written for the exact string ".env" miss every suffixed variant, and those variants hold the credentials that matter most, because they are the ones production actually uses.
- The repository is private. Does that make a committed .env safe?
- It reduces the exposure without removing it. The file is still in the history, so it reaches every clone, fork and mirror, it survives a later commit that deletes it, and a repository that is made public later publishes the whole history at once. Secrets that were ever committed are worth rotating on that basis alone.
- Why does the free scan not check this on any site?
- Because requesting a path nothing links to is active probing rather than passive reading, and doing it to a third-party domain is the line between a scanner and an attacker. The check runs in the Deep Audit after domain ownership is verified, which is also why a clean free report never claims a site has no exposed .env.
Related checks
- Exposed secrets and API keysThe rest of the catalogue: config files, private keys, cloud credentials, database dumps and the backups an editor leaves behind.
- Exposed .git directoryThe route by which a .env that was deleted months ago is still readable, because the history it was committed to is being served.
- Directory listingHow a folder announces the files inside it, so a leak no longer depends on anyone guessing the filename.