What this means
The finding means https://yourapp.com/.git/config, and usually the rest of the folder, returns real content. Git's own documentation describes what lives there: the object database with all file contents, refs, HEAD, config and the index. With those files a person can reconstruct the repository. Your config file may also contain a remote URL with an embedded access token.
Why it happens
- The site was deployed by running git clone or git pull inside the web root of a server.
- The web server's document root is the repository root, not a build or public folder.
- A copy or upload step (scp, rsync, FTP, a storage bucket sync) included hidden folders.
- A Docker image was built with COPY . . without a .dockerignore, and a static server serves that directory.
- Directory listing being off gives a false sense of safety. Git's file names are predictable, so listing is not needed.
How to fix it
- 1Rotate first. Search your history for secrets (any .env that was committed, keys in config files, tokens in the remote URL) and rotate each at its provider. Assume the full history was downloaded.
- 2Block access to /.git at the web server immediately, using the rule below or your server's equivalent.
- 3Change the deploy so that only build output is copied to the web root. Build in CI or locally, then upload the dist, build or out folder.
- 4Add .git to .dockerignore and exclude it in rsync or upload commands.
- 5Remove the .git folder from the server's web root once the new deploy path works.
- 6Purge committed secrets from history with git filter-repo, as GitHub documents, and force-push.
- 7If the code contained customer data, private keys or anything under contract, tell the people who need to know.
location ~ /\.git {
deny all;
return 404;
}
RedirectMatch 404 /\.git
.git
.env
node_modules
rsync -av --delete --exclude='.git' --exclude='.env*' dist/ user@server:/var/www/yourapp/How to confirm the fix
Request the well-known git paths on your own domain. All should return 404 or 403. Before the fix, /.git/HEAD typically returns a line beginning with ref: refs/heads/.
for p in .git/config .git/HEAD .git/index .git/logs/HEAD; do
printf '%s ' "$p"
curl -s -o /dev/null -w '%{http_code}\n' "https://www.yourapp.com/$p"
done
curl -s https://www.yourapp.com/.git/HEAD | head -n 1Frequently asked questions
My repository is already public on GitHub. Does this matter?
Less, but check that the deployed copy matches. The server's .git can include unpushed branches, local config and credentials in the remote URL that the public repository does not.
I blocked the folder. Do I still need to rotate secrets?
Yes, for anything that was ever committed. Blocking stops new downloads. It does not recall a copy already taken.
Does this affect Vercel, Netlify or similar hosts?
Those platforms deploy build output, so .git is not normally served. The finding is most common on servers and containers configured by hand.