The fix, in one snippet
location ~ /\.git { deny all; return 404; }
# and stop shipping it:
rsync -a --exclude='.git' ./ server:/var/www/
Illustrative values. Change the paths, hostnames and options to match your own site before using it.
The sections below explain what is tested, why it matters and the alternatives.
What we test
Scan.now requests /.git/HEAD. A finding requires an HTTP 200 whose body begins with ref: refs/heads/ or consists of a 40- or 64-character hexadecimal commit hash; an HTML "not found" page returned with status 200 does not match. To rule out a coincidental file, it then requests /.git/config and confirms that it contains a [core] section. Scan.now stops there: it does not enumerate refs, download objects or reconstruct the repository, and it never stores what it retrieves beyond the match itself. The finding is rated critical because reconstruction from these two files is fully automated in public tools and needs no further weakness. Related exposures are covered by exposed-env-file and exposed-backup-files.
Why it matters
An exposed .git directory is a complete copy of your codebase for anyone who asks. Tools such as git-dumper walk from HEAD through the refs and packed objects, whose paths are predictable from their hashes, and rebuild a working tree even with directory listing disabled. The result includes configuration files with database passwords and API keys, the logic of every authentication and authorisation check, and the full commit history, so a secret that was committed and later "removed" is still there. .git/config frequently contains a remote URL with an embedded access token. From source, an attacker moves quickly to SQL injection or logic flaws they can now read rather than guess. Internet-wide scans for /.git/HEAD run constantly, so an exposed repository is usually found within days. The exposed files guide describes the pattern and the other files that travel with it.
How to fix it
The durable fix is to deploy build artifacts, not a git checkout, into the web root. Until then, block every dotfile except .well-known at the server. nginx:
location ~ /\.(?!well-known/) {
deny all;
return 404;
}
Apache 2.4:
<DirectoryMatch "/\.git">
Require all denied
</DirectoryMatch>
RedirectMatch 404 /\.(?!well-known/)
Cloudflare: a WAF custom rule with the expression (starts_with(http.request.uri.path, "/.git")) and action Block stops requests at the edge, but apply the origin rule as well. After blocking, treat the incident as a disclosure: rotate every credential in the repository and its history, check the access log for earlier requests to /.git/, and consider whether the source itself reveals anything that needs fixing:
grep -c '/\.git/' /var/log/nginx/access.log
Verify the fix returns 404 for /.git/HEAD and /.git/config, then re-run the website scanner.
Where this fits
Exposed .git repository is check 2 of 14 that the website vulnerability scanner runs under exposed files and information disclosure, ordered the way they are worth fixing. That ordering is the point: Fixing this one while the check above it still fails buys less than it looks like.
Fix this one first
Above it in the same category sits Exposed .env configuration file (critical), where a .env configuration file is served from the web root. An attacker who has that does not need this, so it is the better use of the same hour.
What fixing this still leaves open
Exposed .git repository closes one route in. Immediately below it: Exposed backup or archive files, where a backup archive, database dump or editor backup copy of a configuration file is downloadable from the web root, exposing source code, credentials and user data; Exposed phpinfo() page, where a phpinfo() page is publicly reachable; Directory listing enabled, where the web server generates an index of files for a directory that has no index page, exposing every file in it, including ones that are not linked from anywhere.
Found in the same scan
The website vulnerability scanner reports this alongside checks from other categories that are at least as serious, including Certificate chain and hostname validation, where the certificate presented for this hostname did not validate: The chain does not reach a trusted root, an intermediate is missing, the name does not match, or the certificate is self-signed or expired, and HTTPS is available, where the site could not be reached over HTTPS on port 443, or the TLS handshake failed. A single run of website vulnerability scanner answers all of them at once.
The first few lines
You are a senior web engineer. I ran a security and SEO scanner against my site and it reported the finding below. Fix it properly rather than suppressing the symptom.
Finding: Exposed .git repository (critical severity)
Scanner check id: exposed-git-directory
The rest of this prompt names the pages and line numbers we found the problem on, the configuration to change, and the constraints a good answer has to respect. It is free, it just needs an account so the work is not scraped wholesale.
Sign in with GoogleSigning in is free and takes one click. We store your email address and nothing else.