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.

Block the path first, then assess. Your first action is to deny all requests to /.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 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 pathWhat it containsRisk
/.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.

Verify the block is active before continuing.
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/config only: Remote URL and config exposed. Serious, but full reconstruction requires packed-refs — less certain.
  • 200 on /.git/packed-refs or /.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.

Absence of log evidence doesn't mean no access. CDN-terminated traffic may not reach origin logs; log rotation may have removed older entries; some platforms don't expose raw access logs at all. If your logs are incomplete or unavailable, assume the worst case and proceed with full credential rotation.

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/config contained a remote URL of the form https://token@github.com/..., revoke that token immediately.
Don't forget to deploy the rotated credentials. After revoking in each provider's dashboard, update them in every consumer: CI/CD pipeline secrets, hosting platform environment variables, partner integrations. A rotated key you forgot to redeploy is an outage.

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 .git exposure 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 .git folder 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/config and related paths on every scan — you'll catch a regression before it has time to be exploited.
  • Include /.git in 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: