Datahorses

Exposed .env, .git and backup files: how secrets leak from websites

By · · 4 min read

Some of the most damaging website leaks involve no hacking at all. A file that was never meant to be public sits in the web root, the server happily sends it to anyone who asks, and that file contains a database password, an API key or the full source code of the site.

The 3 usual suspects are .env files with configuration secrets, .git folders that carry the whole repository history, and backup copies such as site.zip, db.sql or config.php.bak. The OWASP Web Security Testing Guide lists all of them as typical sources of exposed credentials and source code.

You can check your own site for them in a few minutes with curl.

How these files end up public

  • .env files. Many frameworks read secrets from a .env file in the project folder. If the web server's document root is the project folder instead of its public subfolder, the file is served like any other.
  • .git folders. Deploying by running git clone or git pull directly in the web root leaves the .git directory next to the site. With it, someone can rebuild the source code and every past commit, including secrets that were "removed" long ago.
  • Backups and dumps. A quick zip before an update, a db.sql export for a migration, an editor copy like index.php~ or config.php.old. OWASP notes that a backup of a script may be served as plain text instead of being executed, which reveals its source.
  • Leftovers from tools. phpinfo.php, .htpasswd, .DS_Store, Terraform state files and log files show up in the same way.

The common thread is editing or deploying directly on the production server without a step that decides what belongs in the web root.

How to check your own site

Request the most common paths and look at the status code and the size of each response:

for p in .env .git/HEAD .git/config backup.zip site.zip db.sql \
         dump.sql config.php.bak wp-config.php.bak phpinfo.php .DS_Store; do
  printf '%-22s ' "$p"
  curl -s -o /dev/null -w '%{http_code} %{size_download}\n' "https://yourcompany.com/$p"
done

What the results mean:

  • 404 or 403 for every path is what you want.
  • 200 with real content is a finding. For .git/HEAD, real content looks like ref: refs/heads/main. For .env, it looks like lines of NAME=value.
  • 200 with your normal home page or a custom error page is usually a "soft 404". OWASP warns that a hit alone is not a finding: open the response and confirm what it actually contains.

Repeat the check on every hostname you run, including staging and old subdomains. Those are where such files survive longest, as described in our article on forgotten staging and test servers. Web archives can also hold copies of files that were public in the past, so a file you just removed may still have been seen.

If a secret was exposed, rotate first

Deleting the file stops new downloads, but you cannot know who already copied it. Treat every secret in an exposed file as compromised:

  1. Rotate or revoke each password, API key and token in the file. GitHub's guidance on removing sensitive data gives the same order: revoke or rotate the secret first, then clean history.
  2. Remove the file from the server and from any backup folder inside the web root.
  3. Review access logs for requests to that path to understand whether and when it was downloaded.
  4. Check what the key could reach: cloud consoles, payment providers, mail services, and look for activity you cannot explain.

If secrets were committed to a repository, rewriting history (GitHub recommends git-filter-repo) only helps after the secret itself is no longer valid.

How to prevent it

  • Point the document root to the public folder only, never to the project root.
  • Deploy build artifacts, not working copies. A CI pipeline that copies only the files the site needs keeps .git and .env out by design.
  • Block dotfiles at the web server, as shown below.
  • Keep secrets out of files where possible: use your platform's environment variables or a secrets manager.
  • Never store backups or dumps in the web root. OWASP recommends keeping data, logs and configuration files where the web server cannot serve them.
  • Recheck after every infrastructure change, including new servers behind a load balancer.

Blocking dotfiles (while keeping /.well-known/, which certificate renewal needs) looks like this in nginx:

location ~ /\.(?!well-known) {
    deny all;
}

And like this in an Apache site configuration:

RedirectMatch 404 /\.(?!well-known)

A short checklist

  • Run the curl loop above on every hostname you own.
  • Confirm each 200 by reading the content.
  • Rotate any secret that was ever reachable.
  • Block dotfiles and backup extensions in the web server configuration.
  • Move deployments to a pipeline that ships only built files.

While you are in the server configuration, it is a good moment to add the HTTP security headers every business website should send.

How Datahorses helps

Datahorses watches your website from the outside the way an attacker would look at it, and flags exposed .git, .env, backup and configuration files that web archives have recorded on your domains. We do not enter your systems and need no access; your developer gets the exact steps to remove the file and rotate what leaked. See how we guard and why no access is needed.

More articles

All articles