A web server that serves its .git directory lets anyone rebuild the site's full source code and commit history, including every credential that was ever committed and later deleted. It happens when a deployment copies a working tree, hidden folders and all, into the web root, and the server is not told to refuse requests for /.git/. MITRE catalogues the weakness as CWE-527, Exposure of Version-Control Repository to an Unauthorized Control Sphere, and it is one of the most common findings reported through bug bounty and vulnerability disclosure programmes. This guide is for web operations, application security and incident response teams who have just received such a report or found the exposure themselves. It covers confirming the exposure safely, blocking it, working out exactly what was downloadable, rotating the secrets in that history and preventing a repeat. Theft of a repository from a Git host or an insider leak is a broader case covered by the Source Code Leak Response Playbook.
Key takeaways
- Exposure means full history, not one file. If
/.git/HEAD,/.git/configand the object store are readable, assume an outsider can reconstruct every commit, branch and tag that the deployed copy held. - Block first, investigate second. A deny rule at the web server, load balancer or CDN takes minutes and is safe to apply before the investigation starts.
- Rotate every secret in history. Credentials removed in later commits are still in the object store; deleting files or rewriting history does not make leaked secrets safe again.
.git/configis the first credential to check. Remote URLs with embedded usernames and tokens give access to the source host, and from there to pipelines and deployment keys.- Fix the deployment, not just the server. Ship build artefacts without version control metadata, and test every hostname and raw IP that serves the site.
How the incident shows up
Most organisations learn about an exposed .git directory from someone outside: a researcher, a bug bounty submission, a customer or an attack surface scan. Inside, the signal is usually in web server access logs. The .git layout is documented in Git's own gitrepository-layout reference, which explains why a handful of well-known paths is enough to start a reconstruction.
| Path requested | What it reveals | Why it matters |
|---|---|---|
/.git/HEAD | The current branch reference | A plain-text reply confirms exposure |
/.git/config | Remote URLs, branch settings, sometimes embedded credentials | Direct path to the source host and its secrets |
/.git/index | The list of tracked file paths | Shows the whole file tree, including config files |
/.git/refs/, /.git/packed-refs, /.git/logs/HEAD | Branch and tag tips, reflog of recent commits | Commit IDs to fetch, including history |
/.git/objects/ (loose objects and pack/) | Compressed file contents, trees and commits | The actual source code and history |
Look out for the same pattern beside it. A deployment that copies .git often also copies .env, .svn/, .DS_Store, backup archives and editor swap files. OWASP's test WSTG-CONF-04 lists extensions such as .bak, .old, .swp, .zip and .tar.gz that should never be served, and lists .git/ among paths to check.
Triage questions
- Is it real? Request
/.git/HEADand/.git/configfrom outside your network and confirm the response holds real content, not a custom "not found" page served with status 200. Do this read-only; do not run a full dump against production. - Where else is it served? Check every hostname, staging and preview domain, the origin behind the CDN, and the raw IP addresses. A rule on the CDN does not protect a directly reachable origin.
- How long has it been exposed? Find the deployment that first placed
.gitin the web root, and the first external request for/.git/paths in access logs. - Was the object store fetched? Many requests for
/.git/objects/or/.git/objects/pack/from one client, with 200 responses, indicate a full reconstruction rather than a probe. - Which repository and which commits? On the server, the exposed directory itself tells you the remote, branch and object set. That is the exact scope of what was downloadable.
- Is personal data in scope? Source repositories sometimes hold database dumps, test fixtures with real customer data, or logs.
Containment priorities
- Deny the paths at every layer that serves the site. Return 403 or 404 for any request whose path contains
/.git, and do the same for.envand other dotfiles you do not serve on purpose. Apply it at the CDN or load balancer and at the origin. - Remove the directory from the web root. Move
.gitout of the document root, or redeploy from an artefact that never contained it. Keep a forensic copy first. - Revoke credentials in
.git/configimmediately. Remote URLs with embedded tokens or passwords are the shortest path from a read-only leak to write access on the source host. See How to revoke a leaked GitHub personal access token and check what it touched where the remote is GitHub. - Protect deploying branches. If the source host credential had write access, check for pushed changes and require reviewed pull requests on branches that trigger deployment. The CI/CD Pipeline Exploitation 2024 demonstration shows why: credentials from an exposed
.git/configlet a researcher clone the repository, edit the pipeline and gain SSH access to the server on the next run. - Start the secret inventory. Everything in the next section depends on knowing which secrets were reachable.
Deny rule patterns
| Server | Pattern (adapt and test) | Notes from vendor docs |
|---|---|---|
| NGINX | location ~ /\.git { return 404; } | Regular expression locations are checked in order after the longest prefix; a prefix location marked ^~ skips regex checks, so place the rule where it will be reached. return comes from the rewrite module. |
| Apache HTTP Server | RedirectMatch 404 /\.git | RedirectMatch accepts a status code; for codes outside 300 to 399 the URL argument is omitted. |
| CDN or load balancer | Path-match rule blocking /.git and /.env | Covers the edge only; the origin still needs its own rule. |
See the NGINX Security Hardening Guide and the Apache HTTP Server Hardening Guide for the wider baseline.
Eradication
Reconstruct what an outsider could have downloaded, then make every secret in it worthless.
- Work from the server's copy: Clone or copy the exposed
.gitto an isolated analysis host. That object set, not your main repository, defines the exposure, because deployed copies can hold old branches and unreferenced objects. - Scan all history: Run a secret scanner across every commit, branch, tag and dangling object, not just the working tree. Include
.envfiles, configuration templates, CI definitions, infrastructure as code and test fixtures. See the Git Secret Scanning Guide. - Rotate, do not just delete: GitHub's guidance on removing sensitive data says that if the data is a secret, the first step is to revoke or rotate it, and that once rotated, rewriting history may not be needed. Rotate database passwords, API keys, cloud keys, SMTP and payment credentials, signing keys and deployment keys found anywhere in history. The Developer Secret Scanning Triage guide helps prioritise.
- Rewrite history only when needed: If the history also contains data that rotation cannot fix, such as personal data, GitHub documents
git-filter-repo --sensitive-data-removal(version 2.47 or later) and warns about the side effects: every clone must be re-cloned, signatures are lost and pull request diffs break. - Check for follow-on activity: Review source host audit logs, cloud trails and server
authorized_keysfiles for use of the leaked credentials during the exposure window.
| Secret found in history | Rotation action | Where to check for use |
|---|---|---|
Git remote token or password in .git/config | Revoke token; replace with deploy keys or short-lived tokens | Source host audit log: clones, pushes, settings changes |
| Cloud access keys | Deactivate and delete the key; issue role-based credentials | Cloud audit trail for the key ID |
| Database credentials | Change password; restrict network access | Database connection and audit logs |
| Third-party API keys (payments, email, SMS) | Regenerate in the provider console | Provider usage dashboards and logs |
| Application signing or session keys | Rotate keys; invalidate sessions and tokens signed with them | Application logs for forged sessions |
| SSH and deployment keys | Remove from servers and source host; issue new keys | authorized_keys, SSH logs, deployment history |
Recovery
- Change the deployment method. Deploy build artefacts (container images, packages or archives) assembled without version control metadata, rather than running
git pullin the web root or syncing a working tree. - Keep secrets out of the repository. Load them at runtime from a secrets manager or the platform's secret store, and keep data, logs and configuration outside the web root, as OWASP's remediation for WSTG-CONF-04 recommends.
- Keep credentials out of remote URLs. Use credential helpers, deploy keys or federated identity for servers that must fetch code.
- Add a pre-release and continuous check. Test that
/.git/HEAD,/.git/configand/.envreturn 403 or 404 on every hostname and IP after each deployment, and schedule an external scan. See the Attack Surface Management Guide. - Close the report. Tell the reporter the issue is fixed and, where your programme allows, credit them. See the Bug Bounty and Vulnerability Disclosure Guide.
Evidence to preserve
| Evidence | Source | What it answers |
|---|---|---|
Copy of the exposed .git with hashes | Web server file system | Exact downloadable scope |
Access logs for /.git/ and /.env paths | Web server, CDN, load balancer, WAF | Who fetched what and when; status codes and byte counts |
| Deployment history | CI/CD runs, configuration management logs | When the exposure began |
| External report and correspondence | Bug bounty platform, mailbox | Discovery time and reporter claims |
| Secret scan output and rotation log | Analysis host, ticketing | What was exposed and when each item was rotated |
| Source host and cloud audit logs | Git host, cloud provider | Whether leaked credentials were used |
Check log retention before you start: CDN logs in particular may be short-lived or disabled by default. The Web Server Access Log Detection Guide covers query patterns.
Notification triggers
- Personal data in the repository: Under GDPR Article 33, a personal data breach likely to result in a risk to individuals is notified to the supervisory authority without undue delay and, where feasible, within 72 hours of becoming aware. Article 33(5) requires the breach and your decision to be documented even when you do not notify.
- Credentials used against other systems: If logs show leaked credentials reached customer data, the notification analysis moves to that system. Use the Data Breach Notification Checklist.
- Third-party credentials: Where keys belong to a partner or supplier integration, tell them so they can check their side.
- Sector rules: NIS2, DORA and similar regimes apply where the exposure becomes a significant incident. See the Cyber Incident Reporting Requirements Map.
Lessons from incidents
- Scale: The Misconfigured Git Servers 2026 study counted nearly 5 million IP addresses serving
.gitdata, about 5% with deployment credentials in.git/config. Attackers scan for this continuously, so exposure windows are short. - Configuration files are credential stores: In the Indian Government Breach 2021, researchers in a disclosure programme found exposed
.gitdirectories and.envfiles that together yielded 35 credential pairs and private keys. - The companion file matters: The 230 Million AWS Cloud Environments Campaign 2024 shows what happens when exposed
.envfiles hold long-lived cloud keys: a scan-and-extort operation built on customer-side configuration. - Read access becomes write access: The CI/CD Pipeline Exploitation 2024 demonstration turned
.git/configcredentials into pipeline changes and server access.
Detection and measurement
| Metric (NHI Mgmt Group suggestion) | Target |
|---|---|
Public hostnames and IPs returning content for /.git/HEAD or /.env | Zero, checked at least weekly |
| Time from report to deny rule in place | Under 4 hours |
| Secrets found in exposed history that are rotated | 100%, with rotation time recorded |
| Deployments using artefacts without version control metadata | Trending to 100% |
Alert on 200 responses for /.git/ paths in web logs | Enabled on every internet-facing site |
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| MITRE CWE | CWE-527 Exposure of Version-Control Repository to an Unauthorized Control Sphere | The weakness itself |
| OWASP WSTG | WSTG-CONF-04 Review Old Backup and Unreferenced Files for Sensitive Information; WSTG-INFO-05 Review Web Page Content for Information Leakage | How testers find it; remediation guidance |
| MITRE ATT&CK | T1213.003 Code Repositories; T1552.001 Credentials In Files | Collection of source and credentials from the exposed history |
| NIST SP 800-53 Rev 5 | CM-7 Least Functionality; IA-5 Authenticator Management; IR-4 Incident Handling; SA-15 Development Process, Standards, and Tools | Serve only what is needed, rotate authenticators, handle the incident, fix the build |
| NIST CSF 2.0 | RS.MI-01; RS.AN-03; RC.RP-01 | Contain, analyse, recover |
Practitioner checklist
- Confirm the exposure read-only from outside, on every hostname, origin and IP.
- Deny
/.gitand/.envat the edge and at the origin, then verify with a fresh request. - Take a forensic copy of the exposed directory and preserve access logs before cleaning up.
- Revoke credentials in
.git/configfirst, then check the source host for pushes and settings changes. - Scan the full exposed history, including old branches and dangling objects, for secrets.
- Rotate every secret found, with owner and time recorded, before considering a history rewrite.
- Review cloud, database and provider logs for use of the leaked credentials during the exposure window.
- Change the deployment to ship artefacts without version control metadata and keep secrets outside the web root.
- Add a post-deployment test and a scheduled external scan for dotfile exposure.
- Record the notification decision and close the loop with the reporter.
Standards and references
- MITRE: CWE-527 Exposure of Version-Control Repository to an Unauthorized Control Sphere
- OWASP: WSTG-CONF-04 Review Old Backup and Unreferenced Files for Sensitive Information
- OWASP: WSTG-INFO-05 Review Web Page Content for Information Leakage
- Git: gitrepository-layout
- GitHub Docs: Removing sensitive data from a repository
- NGINX: ngx_http_core_module (location)
- Apache HTTP Server: mod_alias (RedirectMatch)
- EUR-Lex: Regulation (EU) 2016/679 (GDPR)
Related NHI Mgmt Group resources: Source Code Leak Response Playbook · Leaked Credential Response Playbook · Git Repository Security Guide · Secret Sprawl Measurement Guide