WhoopsBot

Guides · Updated 2026-10-22

How to check whether /.env or /.git is public

How to tell a real public /.env or /.git from a single-page app's HTML fallback, what to do if the files are reachable, and how to rotate what leaked.

What you are looking for

A public /.env file means the secrets you meant for the server are available to anyone who can guess the path. A public /.git folder is worse in a different way. It is the repository itself: branch names, remotes, and, if the objects are reachable, the source and every secret that was ever committed. Both mistakes come from deploying the project directory instead of the built output, or from a web server that was never told to refuse dotfiles.

You can check a single site in a minute with curl. You do not need an account on the host. You do need to know the difference between a real file and the HTML page many apps return for every unknown path.

Check /.env

Run curl -sI https://your-app.example/.env and read the status and the content type. Then run curl -s https://your-app.example/.env and look at the body. A leak is a 200 whose body is lines like DATABASE_URL=postgres://... and SECRET_KEY=..., not a web page. Two or more lines that look like NAME=value, with a content type that is not HTML, is enough to treat the file as public.

A 404 is the result you want. A 403 is also fine: the server knows the path and is refusing it. A 200 that starts with <!doctype html or <html is not a leaked env file. Vite, Next.js static exports, and lots of other single-page hosts send index.html for any path so the client router can take over. That HTML is not your secrets. It is a fallback. Treating it as a leak will send you rotating keys that were never exposed.

Check the apex and the www host if both answer, and check the host you actually published, not only localhost. A file that is ignored by git can still be sitting in the folder you uploaded.

Check /.git

Request https://your-app.example/.git/HEAD. A public repository answers with a line like ref: refs/heads/main. Request https://your-app.example/.git/config next. A real config file contains a [core] section or a [remote "origin"] section, sometimes with the URL of the private remote. Either response means the .git directory is on the public internet.

HEAD alone is already enough to worry. From a public .git directory, the objects can often be downloaded and the tree reconstructed. That includes old commits. Deleting .env today does not help if .env was committed last month and the objects are still fetchable. The same HTML-fallback rule applies: if /.git/HEAD returns your homepage HTML, the path is not a git file.

If either one is a real file

Take the files off the internet first, then rotate. Leaving the file up while you draft new keys just extends the window. Remove the project root from the web root. Deploy the build output, the directory your framework calls dist, build, or .next's static output, not the folder that contains .env and .git. On nginx, refuse dot paths except the ones you intend to serve. A practical rule is to deny paths under /. except /.well-known/, which certificate and security.txt clients use. On a static host, fix the publish directory and delete the stray upload.

Confirm the fix with the same curl commands. You want a 404 or a 403, and you want the body to stop being KEY=value lines or ref: refs/. Then assume every secret that was in the file, or in the git history you just exposed, has been copied. You will not get a reliable list of who downloaded it.

Rotate what was in there

Change each secret in the system that issued it, not only in your notes. A database password means a new password in the database and in the server environment. A Supabase service role key means rotate it in the Supabase API settings and update the server that uses it. Stripe, email, and cloud keys mean roll them in those dashboards and revoke the old value. A key that remains valid is still the leaked key, even after you deleted the file.

If .git was public, look through the history for tokens before you declare the list complete. Search old commits for KEY=, sk_live_, service_role, and BEGIN PRIVATE KEY. Rotate anything you find, including values you have since removed from the working tree. Force-pushing a cleaned history does not help while the old objects are still downloadable from the public folder. Close that hole first.

Afterward, make the leak harder to repeat. Keep .env out of the publish directory. Keep the git metadata out of it too. Add a deploy check that requests these three paths and fails the release when they return a real file.

Let the free check request them

The free check on the WhoopsBot homepage does this for one URL. It requests /.env, /.git/HEAD, and /.git/config on that origin. It only flags /.env when the response is HTTP 200, is not an HTML page, and contains at least two lines that look like environment assignments. It flags .git when HEAD looks like a git ref or the config file looks like git config. A single-page app's HTML fallback is not reported as a leak.

The same check also looks at that page's JavaScript for a few obvious tokens, including a Supabase service_role key, and it notes missing security headers. It does not crawl the rest of the site, log in, or submit a form. Paste the public origin, read the result, and if a file is exposed, take it down and rotate before you do anything else. If you want that request to run again tomorrow without you remembering, a saved flow can open the site on a schedule. The exposure check itself is the free one-page look.

Check the published page.

No card needed. Free plan forever. Cancel anytime.