← Back to the Git Exposure Checker
Typing https://example.com/.git/HEAD into a browser and reading what comes back is
a real, fast way to spot the obvious case: a plain 404 means nothing is there, and a line reading
ref: refs/heads/main is unmistakably a real, exposed repository. It is also the
method most people reach for first, and it genuinely works for that one shape of response. This
page names no product or company being compared against — "checking by hand" is a generic
manual habit, not a competing tool.
| Case | Checking the URL by hand | This tool |
|---|---|---|
A plain 404, or a login page, at .git/HEAD | Correctly reads as "not exposed" | Correctly reads as "not exposed" |
The obvious case: body reads ref: refs/heads/main | Correctly reads as "exposed" | Correctly reads as "exposed" |
| A site that answers every unknown path with HTTP 200 and a custom error page (a "soft 404") | Sees status 200, easy to misread as exposed | Reads the body; a generic error page does not match real git content, so it is reported clean |
A detached HEAD — a bare 40-character commit hash instead of a ref: line | Easy to see an unfamiliar hex string and move on, missing a real exposure | Treats a bare 40-character hex string as real git-internal content, the same as a ref: line |
Corroborating with .git/config too | An extra request most people skip once HEAD looks clean or looks exposed | Always checks both, and reports when only one corroborates the other |
| Repeatable in CI on every deploy | Manual, point-in-time | CLI returns a real exit code |
False positive. A site answers every unknown path, including
.git/HEAD and .git/config, with HTTP 200 and a generic
<html><body>404 - Page Not Found</body></html> page — a "soft
404", common on shared hosting and several static-site setups. Reading the status code alone
says "exposed"; neither body matches a real .git/HEAD or .git/config
file, so this checker correctly reports the site clean.
False negative. A site's .git/HEAD returns HTTP 200 with the body
4b825dc642cb6eb9a060e54bf8d69288fbee4904 — a bare 40-character commit hash (a
detached HEAD), not the ref: refs/heads/main line most people know to look for, and
.git/config happens to 404. A person scanning for the familiar ref:
format can see an unrecognised string of hex digits and conclude nothing is there. This checker's
classification treats a bare 40-character hex string as real git-internal content on its own, so
it still reports the directory exposed.
If you only need a one-off sanity check on a site you already suspect, and you know to read the body rather than the status code, looking is fine — it costs nothing and you are already there. The gap this tool closes is correctness on the two cases above, and repeatability: the same body check, run again on every deploy, with an exit code a CI pipeline can act on.
Visiting https://example.com/.git/HEAD directly is a real, fast way to spot the obvious case: a plain 404 or a login page means nothing is there, and a line reading "ref: refs/heads/main" is unmistakably a real, exposed git repository. That single glance genuinely works for that one shape of response.
On a site configured to answer every unknown path with HTTP 200 and a custom error page -- common on shared hosting, several static-site setups, and single-page apps that route unknown paths back to index.html. A person checking only the status code sees 200 at .git/HEAD and concludes the repository is exposed, when the body is actually a generic "page not found" message that happens to carry a 200 status. This checker reads the response body and requires it to actually match a real .git/HEAD file (a ref line or a bare 40-character commit hash) before reporting a finding, so a soft-404 page is correctly reported clean.
When the repository's .git/HEAD is a detached HEAD -- a bare 40-character hex commit SHA such as 4b825dc642cb6eb9a060e54bf8d69288fbee4904, rather than the familiar "ref: refs/heads/main" line most people know to look for. A person expecting the ref: line can glance at a bare hex string, not recognise it as anything in particular, and move on -- missing a real, exposed .git directory. This checker's classification treats a bare 40-character hex string as real git-internal content on its own, the same as the ref: form, so a detached-HEAD exposure is still reported as a finding.
A browser check is a point-in-time, manual look at one URL. A deploy pipeline, a new static-site host, or a webserver config change can expose (or stop exposing) a .git directory at any point after the last time someone happened to check. The CLI here returns a real exit code from the same body classification, so it can gate a deploy the same way a linter or test suite does, rather than depending on someone remembering to look again.