GuideTechnical SEO
Is your .env or .git folder public? How to check in 30 seconds
Two curl commands show whether your .env or .git folder is public. What a leak looks like, why deleting the file is not enough, and the rotation steps.

To check whether your .env or .git folder is public, request https://yoursite.com/.env and https://yoursite.com/.git/HEAD: both must return 403 or 404. If either returns 200 with the file's real contents (KEY=value lines, or ref: refs/heads/main), treat every secret in it as stolen, rotate them all, and only then fix the server.
It takes 30 seconds, and it is worth doing on every production and staging host you own, because the file that leaks is rarely on the one you remembered to check.
How do you check if your .env or .git folder is public?
Run two commands from any terminal. The first prints only the status code for /.env; the second prints the body of /.git/HEAD, which is a single line on any Git checkout. A 403 or 404 on both is a pass. Anything else needs the body read before you decide.
curl -s -o /dev/null -w '%{http_code}\n' https://yoursite.com/.env
curl -s https://yoursite.com/.git/HEAD
Then read what came back:
| Request | Safe response | What a leak looks like |
|---|---|---|
/.env | 403 or 404 | 200 with lines like DATABASE_URL=postgres://... or STRIPE_SECRET_KEY=... |
/.env.local, /.env.production, /.env.backup | 403 or 404 | Same as above; build tools create several variants |
/.git/HEAD | 403 or 404 | 200 with ref: refs/heads/main (or another branch name) |
/.git/config | 403 or 404 | 200 with a [remote "origin"] section and your repository URL |
One false alarm is common. A single-page app often answers every unknown path with index.html and a 200, so /.env returns your homepage. That is not a leak. Check with curl -s https://yoursite.com/.env | head -5: HTML means the fallback answered; KEY=value lines mean the file is public.
Run the same commands against www, any staging or preview hostnames, and old subdomains still on a server you control. OWASP's testing guide lists .env, .pem, .key, .htpasswd and .npmrc among files that should generally never be publicly served, and it lists .git/ among the paths to probe, so those are worth a request too.
Why is a public .git folder so dangerous?
Because it is the whole repository, not a config file. The Pro Git book describes the .git directory as containing almost everything Git stores and manipulates, and says copying it gives you nearly everything you need to back up or clone the repository. A served .git folder lets anyone rebuild your source code and its history.
What that exposes:
- Every file in every commit, including a
.envor key file that was committed once and deleted in a later commit. Deleting a file removes it from the latest version, not from history. objects/, which holds the content, andrefs/, which points at every branch and tag.config, which names your remote and sometimes contains a token embedded in the remote URL.- Your server-side code, which shows an attacker your routes, queries and admin paths.
Directory listing does not need to be on for this to work. The file names inside .git are predictable (HEAD, config, refs/heads/main), and each file points to the next, so the repository can be fetched one request at a time.
How do .env and .git files end up public?
They get copied into the folder the web server serves. Static hosts and web servers serve whatever is in that folder, with no idea which files were meant to be private. OWASP's advice is to design applications so they do not create or rely on files stored in the web directory trees the server exposes.
The usual ways it happens:
- The whole project folder is the web root. A server pointed at the repository checkout (
/var/www/app, with.gitand.envinside it) serves both unless told not to. - A deploy copies everything:
rsync -a ./ server:/var/www/,scp -r, an FTP upload of the project folder, or a DockerfileCOPY . .into a directory a web server then serves. - A file lands in the build output. Vite copies everything in
public/to the root ofdistas-is, so a.envsaved there ships to production. Netlify serves the publish directory, and every static host serves its output folder the same way. - A backup is left behind:
.env.bak,.env.old,site.zipor a database dump in the web root.
Check the build output itself before deploying: find dist -name ".*" (or .next, build, out, whatever your output folder is) should list nothing sensitive.
Are NEXT_PUBLIC_ and VITE_ variables exposed?
Yes, by design, to every visitor. Next.js inlines any variable prefixed NEXT_PUBLIC_ into the JavaScript bundle at build time, and Vite exposes every VITE_ variable in client-side code after bundling. Vite's docs say these "should not contain sensitive information such as API keys."
The rule is simple: a prefixed variable is public, so only values meant for browsers belong there.
| Fine with a public prefix | Never with a public prefix |
|---|---|
| Analytics or tag IDs | Payment provider secret keys |
| A payment provider's publishable key | Database URLs and passwords |
| A Supabase URL and publishable (anon) key, when every table has row-level security | A Supabase secret (service role) key |
| Your site's own public URL | AI provider API keys |
| Feature flags with no sensitive meaning | Webhook signing secrets, JWT secrets, OAuth client secrets |
To check what actually shipped, build the app and search the output for key prefixes you know, for example grep -rn "sk_live\|service_role" .next/static dist 2>/dev/null. Next.js also notes that NEXT_PUBLIC_ values are frozen at build time, so a key you remove from the environment stays in the old bundle until you rebuild and redeploy. That makes rotation the real fix here too.
Do source maps leak your code too?
They can. A source map lets anyone rebuild your original, unminified client code, comments and file paths included. It never contains server secrets, but it does hand over the logic of your front end and often the names of internal endpoints. Both major build tools leave them off in production unless you opt in.
- Next.js: the docs say production browser source maps are disabled "to prevent you leaking your source on the client" unless you set
productionBrowserSourceMaps: true, in which case Next.js serves the.mapfiles next to your JavaScript. - Vite:
build.sourcemapdefaults tofalse.truewrites a separate.mapfile with a comment pointing to it,'inline'embeds the map in the bundle, and'hidden'writes the file without the comment, for uploading to an error tracker.
To check, open one of your production JavaScript files, look for a //# sourceMappingURL= comment at the end, and request that URL. A 200 means the map is public. If you need maps for error reporting, use hidden maps, upload them privately, and keep them out of the deployed folder.
What do you do if your .env or .git was exposed?
Rotate first, fix second. GitHub's guidance for leaked secrets is that the first step is to revoke or rotate them; removing the file or rewriting history does nothing about copies already made. Work through it in this order:
- Block the path right away (see the next section) so no new copies are made.
- List every secret the file held, and every secret ever committed to the repository if
.gitwas exposed. Check old commits:git log -p --all -- .envshows every version of a tracked.env. - Rotate each one at its provider: generate a new key, deploy it, then revoke the old one. Include database passwords, payment and email keys, OAuth client secrets, JWT signing secrets and webhook secrets.
- Check each provider's logs and dashboards for activity you do not recognise from the moment the file became reachable.
- If secrets were committed, rewrite history with
git filter-repo(GitHub says you need a version with the--sensitive-data-removalflag, 2.47 or later), and remember GitHub's warning that you cannot remove data from other people's clones. - Purge your CDN cache for the exposed paths, since a cached copy can outlive the fix on the origin.
- Add a check to your deploy so it cannot happen again: fail the build if the output folder contains a dotfile, or run the two curl commands after every deploy.
How do you block dotfiles on the server?
Keep them out of the served folder first, and add a server rule as a backstop. The rule should refuse every path beginning with a dot, except /.well-known/, which certificate renewal and security.txt rely on.
For Nginx, inside the server block:
location ~ /\.(?!well-known) {
return 404;
}
On platforms like Vercel and Netlify, which serve a build output rather than your repository, the fix is to keep secrets out of that output: store them in the platform's environment settings, keep .env files out of public/, and make sure *.local is in .gitignore, as Vite's docs advise.
A leaked .env is the single most damaging failure in the vibe-coded app security checklist, which covers the rest, from keys in the client bundle to missing row-level security. The security headers checklist covers the response headers to send once the files are locked down, and the comparison of website security scanners shows which free tools probe these paths at all.
To check your live site for these files without typing each path, run the free scan on LaunchScaler. It needs only your URL and no account, and its free security checks request /.env and its common variants, /.git/HEAD and /.git/config, published JavaScript source maps, database dumps and archives, open directory listings and unauthenticated admin or debug endpoints, and they read the body, so an HTML fallback page is not reported as a leak. They run with 156 checks across 6 of its 7 categories.
Questions, answered
What people ask about this
01How do I check if my .env file is exposed?
- Run curl -s -o /dev/null -w '%{http_code}' https://yoursite.com/.env. A 403 or 404 is safe. A 200 needs a second look: if the body contains KEY=value lines, your secrets are public; if it is your homepage HTML, a single-page app fallback is answering and nothing leaked.
02Why is an exposed .git folder dangerous?
- The .git directory holds almost everything Git stores: every file version, every commit and the remote configuration. Anyone who can download it can rebuild your source code and history, including secrets that were committed once and deleted later.
03Is deleting an exposed .env file enough?
- No. Assume every value in it was copied the moment it was reachable. Rotate every key, token and password the file ever held, then check each provider's logs for use you do not recognise.
04Are NEXT_PUBLIC_ and VITE_ variables secret?
- No. Next.js inlines NEXT_PUBLIC_ values into the JavaScript sent to the browser at build time, and Vite's docs say VITE_ variables end up in the client bundle and should not contain sensitive information. Only put values there that any visitor may see.
Sources
- OWASP WSTG: Review Old Backup and Unreferenced Files for Sensitive Informationwstg.owasp.org
- Pro Git: Git Internals, Plumbing and Porcelaingit-scm.com
- GitHub Docs: Removing sensitive data from a repositorydocs.github.com
- Next.js: Environment variablesnextjs.org
- Vite: Env variables and modesvite.dev
- Vite: Static asset handling (the public directory)vite.dev
- Next.js: productionBrowserSourceMapsnextjs.org
- Vite: Build options (build.sourcemap)vite.dev
- Supabase: Understanding API keyssupabase.com
- Netlify: Custom headers (publish directory)docs.netlify.com
- nginx: ngx_http_rewrite_module (return)nginx.org

