Two files cause some of the most complete leaks on the web, and neither needs any skill to exploit:
/.envusually holds every secret your app uses: database passwords, API keys, signing secrets./.git/holds your repository. With it, anyone can rebuild your source code and its full history, including secrets you deleted in later commits.
They get deployed by accident: a build that copies the whole project folder, a static host serving the repository root, or a manual upload of "everything".
Short answer: request
https://your-app.com/.envandhttps://your-app.com/.git/HEADon your own site. If either returns the actual file rather than your normal page or a 404, remove it from the deployment, rotate every secret it contained, and block dotfiles at your host.
How to check your own site
Use a terminal so you see exactly what the server returns:
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" https://your-app.com/.env
curl -s https://your-app.com/.git/HEAD
What the answers mean:
| Response | Meaning |
|---|---|
404 |
Not deployed. Good. |
200 with text/html and your app's page |
Your host serves the app for unknown paths. Not the file. |
200 with lines like DATABASE_URL=… |
The file is public. Act now. |
/.git/HEAD returns ref: refs/heads/main |
Your repository is downloadable. Act now. |
Also check the usual leftovers: /.env.local, /.env.production, /.env.bak, /config.json, /backup.sql, /dump.sql and /db.sqlite.
Why single-page apps make this confusing
Most React, Vue and Svelte hosts answer every unknown path with 200 OK and your index.html, so the client-side router can take over. That means a quick look in the browser can mislead you both ways: /.env "loads" but shows your homepage (not a leak), or you assume everything is a 200-page and miss the one path that really is a file.
Look at the response body, not only the status code. A real .env is plain KEY=value lines. A real .git/HEAD is a single ref: line or a commit hash.
Fixing it
-
Remove the files from what you deploy. Deploy only your build output (
dist/,build/,.next/,out/), never the project root. Keep.env*files out of yourpublic/folder: everything in it is copied to the live site as-is. -
Block dotfiles at the host or server. For example, in nginx:
location ~ /\.(?!well-known) { deny all; }Most managed hosts (Vercel, Netlify, Cloudflare Pages) only serve your build output, so a leak there usually means the file was inside it.
-
Rotate every secret the file contained. Assume each one was copied. Create new keys, revoke the old ones, and check provider logs for use you do not recognise.
-
If
.gitwas exposed, treat the whole history as public. Every secret ever committed, even one removed years ago, must be rotated. Rewriting history afterwards does not help; someone may already have a copy. -
Keep
.envout of git going forward. Add.env*to.gitignore(keep a.env.examplewith placeholder values instead), and use your host's environment variable settings for real values.
Where this overlaps with other leaks
A .env file is often where frontend build variables live too. If it contains VITE_… or NEXT_PUBLIC_… values, those are also compiled into your JavaScript and visible to every visitor, deployed .env or not. See which keys are safe to ship.
Frequently asked questions
.env is in my .gitignore. Am I safe?
From git, yes. But .gitignore does not affect what your build copies or what you upload. Check the live site.
My host returns my homepage for /.env. Is that a problem?
No. That is the single-page-app fallback. The problem is only when the actual file content is returned.
How does PreflightX check this without downloading my data?
It requests a fixed list of well-known paths such as /.env, /.git/HEAD and /.git/config, and recognises a real file by its content, never treating your app shell as a file. For database files it reads only the first 512 bytes, enough to recognise the format, and reports credentials with their last four characters only.
Check what your app exposes right now
PreflightX runs a read-only scan of your live app's public surface: exposed keys in your JavaScript, readable Supabase tables, open storage buckets, deployed .env and .git files, source maps and missing security headers. Every finding is free to see.