Is your .env file or .git folder public? How to check and fix it
By Obscura Security · Updated · nginx, Next.js, Vite, Docker
Short answer
If https://yoursite.com/.env or https://yoursite.com/.git/HEAD returns real content instead of a 404, anyone can download your secrets or your whole source history. Serve only your build output folder, add deny rules for dotfiles and backup files, then rotate every secret that was reachable.
Some files exist to run your app, not to be downloaded: .env holds your passwords and API keys, .git/ holds your project’s full history, and backups and source maps hold code. If your web server can reach them, so can everyone else.
Why this happens
- The site is deployed by copying the whole project folder into the web root with FTP,
scporrsync, so.envand.gitgo along. - A Docker image uses
COPY . .and serves the result, with no.dockerignore. - A web server such as nginx has its
rootset to the project folder instead of the build output folder. - A
.env,.sqldump orbackup.zipwas put into apublic/orstatic/folder, and everything in that folder is served as-is. - A quick fix left
config.php.bakordatabase.sqlon the server.
Automated scanners request these paths on public sites constantly, so assume a file that was reachable has been found.
How to check your own app
Do this against your own site only.
- Request the usual suspects. A correct site returns
404or403for each:
curl -s -o /dev/null -w "%{http_code} /.env\n" https://YOUR-SITE.com/.env
curl -s -o /dev/null -w "%{http_code} /.env.local\n" https://YOUR-SITE.com/.env.local
curl -s -o /dev/null -w "%{http_code} /.git/config\n" https://YOUR-SITE.com/.git/config
- Check the content, not just the status. Many single-page apps answer
200with their homepage for any path:
curl -s https://YOUR-SITE.com/.env | head
curl -s https://YOUR-SITE.com/.git/HEAD
If the first shows KEY=value lines, or the second shows ref: refs/heads/main, the file is public.
- Try other common names:
/backup.zip,/db.sql,/dump.sql,/config.php.bak,/.env.production. - Check for source maps. In the browser’s Network tab, find one of your JavaScript files and add
.mapto its URL. If you get a large JSON file withsourcesContentin it, your source code is public.
The fix
1. Serve only the build output
Point the web server at the folder containing built files, such as dist, build or out, never the project root.
server {
listen 80;
server_name yoursite.com;
root /var/www/myapp/dist; # build output only
}
On Vercel, Netlify and Cloudflare Pages, set your environment variables in the project’s dashboard, not in files, and don’t put .env files inside public/ or static/.
2. Add deny rules as a second layer
In nginx, refuse dotfiles and common backup extensions. Keep /.well-known/ working, since certificate renewal can need it.
location ~ /\.(?!well-known) {
deny all;
}
location ~* \.(bak|old|orig|sql|sqlite|log|swp|map|env)$ {
deny all;
}
location ~ ~$ {
deny all;
}
Reload with sudo nginx -t && sudo systemctl reload nginx. Then repeat the curl checks. If you use location / blocks with their own rules, make sure these still win. A regex location beats a plain prefix location.
3. Keep the files out of the deployment
- Add
.env*and.gitto.dockerignore. - Add
.env*to.gitignore. If.envwas ever committed,git rm --cached .envstops tracking it, but it stays in history. - Delete leftover
.bak,.sqland.zipfiles from the server.
4. Turn off production source maps
Next.js leaves browser source maps off unless you set productionBrowserSourceMaps: true. Vite’s build.sourcemap defaults to false. Create React App generates them unless you set GENERATE_SOURCEMAP=false. If you upload maps to an error-tracking service, upload them there and don’t publish them.
5. Rotate everything that was reachable
This step matters most. Treat every value in an exposed .env as leaked: database passwords, Supabase service role key, Stripe secret keys, OpenAI or Anthropic keys, JWT secrets, email provider keys. Generate new ones, update your hosting, and revoke the old ones.
If .git/ was exposed, the whole history was downloadable, including secrets you deleted long ago. Rotate those too. Removing a secret from history with git filter-repo is worthwhile for tidiness but doesn’t undo a leak, because the old copy has already left.
Then check your access log for earlier requests:
grep -E 'GET /(\.env|\.git)' /var/log/nginx/access.log | grep ' 200 '
Mistakes to avoid
- Rotating only some keys. If you can’t tell which were in the file at the time, rotate all of them.
- Checking the status code only.
200on an SPA may be harmless, and403on one path doesn’t mean another path is blocked. - Putting secrets in
NEXT_PUBLIC_orVITE_variables. Those are built into the JavaScript that every visitor downloads. See secret API key in frontend. - Fixing the web server but leaving the secret. Deny rules stop future downloads. They don’t undo past ones.
Common questions
- I deleted the .env file from the server. Am I safe now?
- The file is no longer served, but anything that was reachable should be treated as compromised, because automated scanners request these paths on public sites all the time. Rotate every secret that was in the file.
- My site returns 200 for /.env but shows my homepage. Is that a problem?
- Probably not. Single-page apps often return index.html for any unknown path. What matters is the content. If the response is your homepage HTML, nothing is leaked. If it contains lines like KEY=value, it is.
- Are source maps a security problem?
- They expose your original source code, which makes it easier for someone to find endpoints and any key that ended up in the frontend. They aren't a secret leak by themselves, but there is little reason to publish them in production.
- How do I know if someone already downloaded my .env?
- Check your web server's access log for requests to /.env or /.git/ that returned status 200. If you see any, assume the contents were taken. If your host gives you no logs, assume the worst and rotate.
Read next
Written by Obscura Security, an Australian practice that checks web apps for problems like this one. If you want a second pair of eyes on yours, email [email protected].