The .git directory is publicly accessible
What it is
The .git/ directory is freely accessible at the root of your website. The file https://example.cz/.git/HEAD returns 200 OK — which means the rest of it is reachable too.
Why it’s a problem
.git/ contains the complete history of your project. An attacker downloads it in under a minute with a tool like GitTools/Dumper or git-dumper. Here’s what they get:
- The complete application source code (including parts that aren’t meant for the web).
- The commit history — including secrets you committed “by accident” and later deleted (a deleted commit stays in the history).
- Configuration files,
.envtemplates, scripts, deploy scripts. - If the repository contains private libraries — those too.
How to fix it
Step 1 — block access
nginx:
location ~ /\.git {
deny all;
return 404;
}Apache (.htaccess):
RedirectMatch 404 /\.gitVerify: curl -I https://example.cz/.git/HEAD should return 404.
Step 2 — deploy without .git
In the long run, .git/ shouldn’t be on the production server at all. Better approaches:
- CI/CD (GitHub Actions, GitLab CI) — the build runs in CI, and only the artifacts are uploaded to production.
git archive—git archive HEAD | tar -x -C /var/www/...transfers only the working tree, without the history.- Capistrano / Deployer / Ansible — standard deploy tools understand “clone, build, symlink” and don’t leave
.gitin the webroot.
Step 3 — rotate any secret that was ever in the repository
If anyone downloaded .git/, they have your history. Assume they know every secret that was ever committed (even deleted ones).
Search the history for old secrets:
# find anything that looks like an API key
git log -p --all -G '(api[_-]?key|secret|password|token)' | head -200
# or use gitleaks
gitleaks detect --source=.Whatever you find — rotate it. Deleting it from the current commit isn’t enough — it stays in the history forever.