My .git/config file got exposed. What should I do?
A visible .git/config almost always means your entire .git directory is exposed — and that can be reassembled into your full source history, including any secret ever committed. Here's how to close it and figure out what's actually at risk.
First: don't panic — but take this one seriously.
Seeing the config file is the tip of it. The real risk is the rest of the folder. The fix is quick; the assessment is where the work is. Don't wipe the directory until you've checked your logs.
// the 60-second version
- Block public access to the whole
.gitdirectory now. - Assume your full source history — and its remote URLs — are public.
- Audit history for committed secrets and rotate every one you find.
- Review access logs to see if the repo was cloned, then stop deploying the
.gitfolder.
01Understand why this is bigger than one file
The .git directory isn't just config — it's the entire repository. Even without directory listing enabled, the file layout is predictable, so tooling can walk it and reconstruct your whole codebase and its commit history from the outside. What that puts at risk:
- Your full source code, current and historical — including code you thought was deleted. Git keeps everything.
- Secrets committed at any point — that API key someone added in a rushed commit two years ago and "removed" the next day is still in history.
- Remote URLs and structure —
.git/configreveals where your real repository lives (GitHub/GitLab org, internal host), and sometimes usernames or tokens embedded in remote URLs. - Internal hostnames, paths and logic that help an attacker plan a deeper move.
History is forever. Deleting a secret in a later commit does not remove it from the repo. If your .git was public, treat every secret that ever lived in history as compromised — not just the ones in your current files.
02Stop the bleeding — deny the .git directory
First, confirm the exposure from outside: requesting /.git/config should currently return file contents. Then block the entire path. For Nginx:
location ~ /\.git { deny all; return 404; }
For Apache:
<DirectoryMatch "/\.git"> Require all denied </DirectoryMatch>
Re-test from an external network and confirm /.git/config, /.git/HEAD and /.git/index all return 403/404. The real fix, though, is step 5: the .git folder should never have shipped to production at all.
03Find out what was in your history
Now audit your own repository the way an attacker would. Scan the entire history, not just the working tree, for anything secret:
# dedicated scanners read every commit: gitleaks detect --source . -v trufflehog git file://. # quick manual sweep across all commits: git log -p | grep -iE "password|secret|api[_-]?key|token|BEGIN.*PRIVATE KEY"
Build a list of every credential you find, regardless of which commit it's in. That list is your rotation worklist for the next step.
04Rotate every secret you found
For each secret surfaced above, generate a new value and revoke the old one at the source — exactly as you would for any leak:
- Remote/deploy tokens — if a personal access token or deploy key sat in a remote URL or committed config, revoke it in your Git host immediately. This is often the most direct path back into your real repo.
- Database & service credentials — rotate at the provider, then update your deployment config.
- API keys & signing secrets — reissue and revoke; don't leave the old key live.
- SSH keys — if a private key was committed, generate a new pair and remove the old public key everywhere it was authorised.
Rotating beats rewriting. You can scrub history with tools like git filter-repo, and you should — but a public copy may already exist. Rotation is what actually neutralises the secret; history rewriting is hygiene on top.
05Was system or auth config in the repo? (PAM, sudoers, SSH)
Repositories often pick up copies of server configuration that should never live in git — and the most dangerous is your authentication stack. If anything from /etc/pam.d/, /etc/security/, nsswitch.conf, sshd_config or sudoers was committed, the leak isn't just a secret: it's a blueprint of how logins are decided on your server.
A leaked PAM config is a big deal. It tells an attacker exactly which modules guard authentication, where accounts are read from (local, LDAP, Kerberos, RADIUS), and any soft spots — and these files frequently embed real secrets: LDAP bind passwords, RADIUS shared secrets, or database credentials used by pam_mysql / pam_pgsql.
If that describes your repo, treat it as an authentication compromise and work through:
- Rotate every secret embedded in those files — LDAP bind passwords, RADIUS shared secrets, and any DB credentials referenced by
pam_*modules. Change them at the source, not just in the file. - Audit the config for the weaknesses it just advertised — permissive rules,
nullok(passwordless) options, disabled lockout/pam_faillock, or any unexpected module in the stack. Tighten them and confirm the file's permissions and ownership are correct. - Force a password reset for accounts reachable through that auth path, and enable MFA where you can — assume the exposed setup made guessing or replay easier.
- Remove the files from the working tree and history (
git filter-repo), and keep system auth config out of git for good.
This is the kind of leak that most often turns into a real intrusion — so the very next thing to do is read your security logs (next step) for logins that shouldn't be there.
06Read the logs — was it cloned, and did anyone get in?
Check your web access logs for the tell-tale fingerprint of someone dumping the directory: repeated requests to .git paths, especially /.git/HEAD, /.git/index, and many /.git/objects/… hits in sequence.
# IPs pulling lots of .git paths = likely a full dump grep "/.git/" /var/log/nginx/access.log \ | awk '{print $1}' | sort | uniq -c | sort -rn
- One or two IPs with hundreds of
.gitrequests strongly suggests an automated clone — treat the whole repo as taken. - Note the timestamps to define your exposure window, then check the services those secrets protect for misuse during and after it.
- Check your Git host's audit log for logins or clones using any leaked token, around the same window.
Check your security / auth logs
If credentials or auth config (above) were exposed, your web logs aren't enough — you need the authentication logs on the server itself. Look for successful logins you can't attribute, bursts of failures, new sessions and privilege escalation around and after the exposure window:
# Debian/Ubuntu: /var/log/auth.log • RHEL/CentOS: /var/log/secure # accepted SSH logins — note unfamiliar users/IPs grep "Accepted" /var/log/auth.log # brute-force attempts (and whether any finally succeeded) grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn # privilege escalation and new sessions grep -E "sudo:|session opened|new user|useradd" /var/log/auth.log
- A successful login from an unknown IP or for a dormant account is your highest-priority signal — treat that account, and anything it could reach, as compromised.
- Unexpected
sudo,useraddor new SSH keys inauthorized_keyscan mean persistence was established. Checklast,lastlogand crontabs too. - If logs look edited or truncated, assume tampering and rely on any off-box/SIEM copies you have.
07Make sure it can't happen again
- Never deploy the
.gitfolder. Deploy a build artifact or anexport, not a live clone. If you must clone on the server, keep it outside the web root. - Add a default deny for
.git(and all dotfiles) at the web-server level, so a stray checkout is never served. - Stop committing secrets. Use
.gitignore, environment variables or a secrets manager, and add a pre-commit secret scanner (gitleaks) to catch them before they land. - Scan periodically — run history scanning in CI so a newly committed secret is flagged on the next push.
Was this guide useful?
These playbooks are free to read and share. If a heads-up ever saved you a bad week, you can say thanks — or jump into the other guides.