Incident response guide
What to do if your .git folder was publicly accessible
A publicly reachable /.git/ directory is one of the most serious web-server misconfigurations possible. Unlike an exposed .env file — which leaks your current credentials — a public .git folder can expose every secret that ever appeared in your entire commit history, plus your full source code. This guide walks through damage assessment and complete remediation, in the right order.
/.git/ at the server level — not to assess damage, not to rotate secrets. Block it now, then work through the steps below to understand what was exposed.
- What it exposes
- Step 1: Block access
- Step 2: Confirm scope
- Step 3: Check access logs
- Step 4: Audit git history
- Step 5: Rotate credentials
- Step 6: Source code disclosure
- Step 7: Notifications
- Prevention
- FAQ
What a public .git folder actually exposes
The .git directory is git's internal database. Every file you have ever committed is stored inside it as a compressed binary object. When that directory is served publicly, an attacker doesn't just see your current code — they can reconstruct your full commit history using automated tools like git-dumper and GitTools.
| Accessible path | What it contains | Risk |
|---|---|---|
/.git/config |
Remote URL (may embed a GitHub/GitLab token in HTTPS clone URLs), branch tracking, author configuration | Critical |
/.git/packed-refs |
SHA hashes for every branch and tag — the index tools use to enumerate the full object store | High |
/.git/objects/ |
The complete object store: every file from every commit as addressable binary blobs. Full source history reconstruction. | Critical |
/.git/logs/HEAD |
Commit log with author names, email addresses, and timestamps for every commit | Medium |
/.git/HEAD |
Current branch name (e.g., ref: refs/heads/main) |
Low |
/.git/COMMIT_EDITMSG |
Most recent commit message — may reveal feature names or internal project details | Low |
These tools don't need directory listing enabled. Once they have /.git/packed-refs, they calculate the exact SHA paths of every object and download them individually — a complete reconstruction from nothing but the exposed file tree.
Step 1: Block access immediately
Deny all requests to /.git/ at the server level before anything else.
Nginx
location ~ /\.git {
deny all;
return 404;
}
# Or block all hidden paths at once:
location ~ /\. {
deny all;
return 404;
}
Add to your server {} block, run nginx -t, then nginx -s reload.
Apache (.htaccess)
<DirectoryMatch "^/.*/\.git">
Order allow,deny
Deny from all
</DirectoryMatch>
<FilesMatch "^\.git">
Order allow,deny
Deny from all
</FilesMatch>
Vercel (vercel.json)
{
"redirects": [{
"source": "/.git/:path*",
"destination": "/404",
"statusCode": 404
}]
}
Netlify (_redirects)
/.git/* /404.html 404
Or add a [[redirects]] block in netlify.toml.
Caddy (Caddyfile)
respond /.git/* 404
# Or for all hidden paths:
respond /\.* 404
Cloudflare (Transform Rules)
Create a Transform Rule or Page Rule matching your-site.com/.git* with action "Block" or a custom 404 response. Available on all plan tiers.
curl -s -o /dev/null -w "%{http_code}" https://your-site.com/.git/config
You should get 404. If you still get 200, the config has not taken effect — check for a CDN cache that needs purging.
Step 2: Confirm which paths were accessible
The scope of the exposure depends on which sub-paths were reachable. Run this audit to establish what was served:
SITE="https://your-site.com"
for path in /.git/config /.git/HEAD /.git/packed-refs \
/.git/info/refs /.git/objects/info/packs \
/.git/logs/HEAD /.git/COMMIT_EDITMSG; do
code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$SITE$path")
echo "$code $path"
done
- 200 on
/.git/configonly: Remote URL and config exposed. Serious, but full reconstruction requirespacked-refs— less certain. - 200 on
/.git/packed-refsor/.git/objects/info/packs: Full repository reconstruction was possible. Assume it happened if accessible for more than a few minutes. - All 404 (after block): Block is active. Now review access logs to determine what was fetched before the block was added.
Step 3: Check access logs for signs of scraping
A git-dumper or GitTools run leaves a distinctive pattern in your logs. On a VPS:
# Nginx
grep '/.git/' /var/log/nginx/access.log | sort
# Apache
grep '\.git' /var/log/apache2/access.log | sort
# Filter by date range
grep '/.git/' /var/log/nginx/access.log | awk '$4 >= "[01/Sep/2026"' | sort
What a scrape looks like: A burst of GET requests to /.git/ sub-paths in rapid succession from the same IP — typically dozens to hundreds of requests within 30–60 seconds. Sequence: /.git/config, /.git/HEAD, /.git/packed-refs, /.git/objects/info/packs, then individual object paths like /.git/objects/ab/cd1234.... If you see this, the repository was almost certainly fully reconstructed.
Step 4: Audit your git history for secrets
A public .git folder exposes every file from every commit — including files deleted from the working tree years ago. Audit the full history, not just the current state:
Search all commits for credential patterns
# Grep all commit diffs for common secret patterns
git log --all -p | grep -iE \
'(API_KEY|SECRET|PASSWORD|TOKEN|sk_live|sk_test|AKIA|ghp_|AIza|pk_live|-----BEGIN)' \
| head -100
# Find .env files ever committed
git log --all --full-history -- "**/.env" "*.env*"
# Show the contents of a deleted .env from a specific commit
git show <commit-sha>:.env
List every file ever committed
# All files ever committed across all branches
git log --all --name-only --format='' | sort -u | grep -v '^$'
# Commits that touched credential file types
git log --all --diff-filter=A --name-only --format='' \
| grep -iE '\.(env|pem|key|p12|json)$'
Use gitleaks for thorough coverage
gitleaks scans the entire git history for 100+ known secret patterns:
# macOS
brew install gitleaks
# Scan all commits, all branches
gitleaks detect --source . --log-opts="--all"
Step 5: Rotate every credential that ever appeared in history
Any secret that appeared in any commit must be treated as compromised. Rotate everything — including keys that were removed from the file years ago:
- AWS access keys — IAM console → Users → Security credentials → Delete and replace. If the key had broad permissions, check CloudTrail for unexpected activity during the exposure window.
- GitHub personal access tokens — Settings → Developer settings → Personal access tokens → Revoke. Check for unexpected OAuth apps, new deploy keys, or repository access changes.
- Stripe, OpenAI, Anthropic, Twilio, Sendgrid — Each provider's dashboard → API keys → Delete old, generate replacement.
- Database connection strings with passwords — Reset the database user's password. Update the connection string in every consumer: platform env variables, CI/CD secrets, other services.
- JWT signing secrets and session secrets — Rotate the value in your environment. Note this invalidates all current sessions — plan accordingly.
- Private keys (SSH, PEM, SSL certificates) — Generate new key pairs. Revoke the old public key from every service where it was registered.
- Embedded HTTPS clone tokens — If
/.git/configcontained a remote URL of the formhttps://token@github.com/..., revoke that token immediately.
Step 6: Assess source code disclosure
Beyond credentials, a public .git folder exposes your source code — every file from every commit. The implications depend on what your code contains:
- Proprietary business logic: Algorithms or workflows core to your business value are now effectively public. Consider how this affects your competitive position and threat model.
- Internal architecture details: Hardcoded internal hostnames, admin URL paths, feature-flag naming, service names — these give an attacker a detailed map of your infrastructure even if no credentials were exposed.
- Comments revealing vulnerabilities: Developer notes like "TODO: fix SQL injection here" or "this check is bypassed for internal IPs" become a roadmap for attack.
- User data in fixtures or test files: Development seeds, test fixtures, or database dumps committed for convenience can contain real user records. If found, this is a personal data breach — proceed to Step 7.
Step 7: Breach notifications and disclosure
Whether you have a legal notification obligation depends on what was in your repository history:
- If git history contained user PII (names, emails, IP addresses, health data, payment references, session tokens): This is a personal data breach under GDPR Article 33, requiring supervisory authority notification within 72 hours if the breach poses risk to individuals. Individual notification is required when the risk is high. US state breach laws (CCPA, SHIELD, etc.) have their own thresholds. Consult legal counsel immediately.
- If credentials to third-party services were exposed: Check your contracts — payment processors and financial services often require breach notification. Revoke the credentials and notify the provider per your agreement.
- Document the timeline regardless: Record when the folder became accessible, when you discovered it, when you blocked it, and what remediation steps you took. This is essential for both internal incident response and any regulatory inquiry.
Re-scan your site with ExposureCheck to verify the fix →
ExposureCheck probes /.git/config, /.git/HEAD, and related paths, confirming they now return 404. It also scans your served bundle for any API keys or secrets that remain in currently deployed code.
How to prevent this from recurring
- Never deploy your project root as your webroot. The most common cause of
.gitexposure is cloning directly into the directory your web server serves. Deploy only the build output (dist/,public/) — never the project root. See the prevention guide for platform-specific instructions. - Add an explicit server-level deny rule. A deny rule for
/.git/in your server config is defense-in-depth against a future deploy mistake. The Nginx and Apache configs in Step 1 above are the permanent fix. - Never commit secrets to git. A public
.gitfolder is most damaging when secrets were ever committed. Keep secrets in environment variables and your platform's secret store. Use LeakCheck to scan code before committing. - Run ExposureCheck after every major deploy. The scanner checks
/.git/configand related paths on every scan — you'll catch a regression before it has time to be exploited. - Include
/.gitin your pre-launch checklist. See the pre-launch audit guide for a complete list of paths to verify before every launch.
Frequently asked questions
Can someone really reconstruct my full repository from a public /.git/ folder?
Yes. Tools like git-dumper and GitTools reconstruct the complete source history by downloading the object store files systematically. Git's object addressing is deterministic — a tool can calculate which object files to request from /.git/packed-refs without needing directory listing enabled. If /.git/config, /.git/HEAD, and /.git/packed-refs were all accessible, assume full repository reconstruction is possible.
What does a git-dumper attack look like in my access logs?
A distinctive burst of GET requests to /.git/ sub-paths in rapid succession from the same IP. The sequence: /.git/config, /.git/HEAD, /.git/packed-refs, /.git/objects/info/packs, then individual object files at /.git/objects/ab/cdef1234.... Dozens to hundreds of requests within a 30–60 second window.
grep '/.git/' /var/log/nginx/access.log | sort -k4
If you see this pattern, the repository was almost certainly fully reconstructed.
My /.git/config returns 200 but /.git/objects/ returns 403 — am I safe?
Not necessarily. A 403 on the objects/ directory listing doesn't prevent object download if an attacker can enumerate hashes. /.git/packed-refs lists exact SHAs for all packed objects, and tools like git-dumper request each object file directly using those SHAs — no directory listing needed. If /.git/packed-refs was accessible, treat the full repository as potentially compromised and follow the full remediation steps.
The .git folder was accessible for several days. What should I assume?
Assume the repository was fully reconstructed. Automated scanners probe /.git/config continuously across the entire IPv4 address space. A folder accessible for more than a few hours has a high probability of being found. Treat this as a confirmed disclosure: rotate every credential across your entire git history and assess source code disclosure per Step 6.
Do I need to notify users or regulators?
It depends on what was in your repository history. If any commit ever contained personal data (user emails, names, addresses, session tokens, health data), this is a personal data breach under GDPR Article 33 — notify your supervisory authority within 72 hours. US state laws (CCPA, SHIELD, etc.) have their own thresholds. If only credentials and source code were exposed, requirements depend on your jurisdiction and contracts. Consult legal counsel with the specifics of what your git history contained.
How do I block .git access on Vercel and Netlify?
Vercel — add to vercel.json:
{"redirects":[{"source":"/.git/:path*","destination":"/404","statusCode":404}]}
Netlify — add to _redirects: /.git/* /404.html 404. Note that these platforms don't normally serve .git at all — exposure typically happens on VPS deployments where the project root was cloned directly into the webroot.
What is the difference between .git exposure and .env exposure?
An exposed .env reveals credentials currently active in your environment. An exposed .git folder can reveal every credential that ever appeared in any commit — including keys rotated months ago that you thought were no longer relevant. It also leaks your full source code history, including deleted files, internal comments, and architecture details never intended to be public. The remediation scope is broader: audit the entire git history, not just the current state of your code, and treat your source as effectively public going forward.
Also in the Copper Bay Labs ship-safety suite
A public .git folder is one of several things to check before and after shipping:
Paste code or your .env and find hardcoded API keys and secrets before they reach the repo.
Scan your live URL for exposed .env, .git, source maps, and bundled secrets. Free, no signup.
Check your security headers — CSP, HSTS, X-Frame-Options — and get a report card with fixes.