Exposed .env, .git and backup files: how secrets leak from websites
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
.envfiles. Many frameworks read secrets from a.envfile in the project folder. If the web server's document root is the project folder instead of itspublicsubfolder, the file is served like any other..gitfolders. Deploying by runninggit cloneorgit pulldirectly in the web root leaves the.gitdirectory 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
zipbefore an update, adb.sqlexport for a migration, an editor copy likeindex.php~orconfig.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:
404or403for every path is what you want.200with real content is a finding. For.git/HEAD, real content looks likeref: refs/heads/main. For.env, it looks like lines ofNAME=value.200with 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:
- 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.
- Remove the file from the server and from any backup folder inside the web root.
- Review access logs for requests to that path to understand whether and when it was downloaded.
- 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
.gitand.envout 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
curlloop above on every hostname you own. - Confirm each
200by 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
Forgotten staging and test servers: the part nobody watches
Staging and test servers are built to be temporary, then quietly outlive the project. Here is how to find yours and decide what to keep, protect or switch off.
Outlook sender requirements since May 2025 and the 550 5.7.515 rejection
If mail to hotmail.com or outlook.com bounces with 550 5.7.515, Microsoft is telling you your domain does not meet its authentication rules. Here is what they require and how to fix it.
PHP2Shell: which PHP versions are fixed and how to check yours
The PHP project fixed 2 critical flaws on 21 October 2026. Here is which versions contain the fix, how to see what your server reports and what to ask your host.