Exposed secrets in GitHub repos are one of the most common and most costly mistakes developers make. API keys, database passwords, and tokens accidentally committed to a repo can be found by automated scanners within minutes of a push. This guide covers how to scan your repo before that happens.
Why Secrets End Up in GitHub Repos
Nobody commits a secret on purpose. It happens as a side effect of moving fast, and the same handful of paths account for almost every leak:
- Hardcoded credentials during local dev that get committed accidentally, usually because it was faster than wiring up environment variables at the time.
.envfiles not added to.gitignore, so the first commit after setting up the project ships the file with real values in it.- Copy-pasting code snippets that include real keys, often from a working example or a Slack message that never got scrubbed.
- Secrets in config files, CI/CD scripts, or commit messages that don't look like the obvious place to check.
- Old commits that still contain secrets even after deletion from the latest code, because removing a file doesn't remove it from history.
What to Scan For
A useful scan needs to cover more than just the word "API_KEY". The credentials worth looking for include:
- API keys (OpenAI, Stripe, AWS, Twilio, and similar)
- Database connection strings
- Private SSH keys and certificates
- OAuth tokens and JWT secrets
- Webhook secrets
.envfiles and their contents
How to Scan Your GitHub Repo for Secrets
There are three practical ways to do this, and they aren't mutually exclusive. Most teams end up combining at least two.
Approach 1: Use a dedicated scanning tool
Tools like GitDoctor scan your entire repo and commit history for exposed credentials automatically, so you don't have to know every secret pattern or remember to run a CLI tool before every push. You get a report of what was found, where it lives, and what to do about it. If you want the fastest path to an answer, scan your repo with GitDoctor and see what turns up.
Approach 2: Use git-secrets or trufflehog locally
If you'd rather run something yourself, git-secrets and trufflehog are both solid CLI options.git-secrets installs as a git hook and blocks commits matching known credential patterns, while trufflehog scans a repo (including its full history) for high-entropy strings and known secret formats. A basic scan looks like:
pip install trufflehog3
trufflehog3 --format json --output findings.json .Both tools are free, run locally, and don't require sending your code anywhere. The tradeoff is you have to run them yourself, on every repo, and keep them updated as new secret patterns emerge.
Approach 3: Enable GitHub's native secret scanning
GitHub's built-in secret scanning is available on public repos for free, and on private repos as part of GitHub Advanced Security on paid plans. You'll find it under Settings > Security > Secret scanning in your repo. It's a good baseline, but it only covers a limited set of partner token patterns (AWS, Stripe, Twilio, and a defined list of others), so it won't catch a custom internal token or an unusual connection string format.
How to Scan Your Full Commit History (Not Just Current Code)
Deleting a secret from your latest commit does not remove it from git history. Git history is permanent unless you explicitly rewrite it. The key you deleted last week is still sitting in the commit where you added it, and anyone with read access to the repo (or anyone who cloned it before you deleted it) can still see it.
This is where most developers get caught out. They notice a secret in the current file, delete it, commit the fix, and consider the problem solved. It isn't. Any scan you run needs to cover all historical commits, not just HEAD, or you're only checking the part of the problem that was already visible.
What to Do After You Find a Secret
Finding a leaked secret is not the end of the process. What you do next matters more than the scan itself:
- Immediately rotate or revoke the exposed credential at the source (AWS console, Stripe dashboard, database provider, wherever it was issued).
- Assume it has already been compromised. Automated scanners monitor public repos continuously, and even a private repo can have leaked through a fork, a CI log, or a former collaborator.
- Then clean the git history. Rotating the key limits the damage, but the old value is still sitting in your commits until you remove it from your git history.
- Force push the cleaned history and notify collaborators so they can re-clone or rebase instead of merging the old history back in.
How to Prevent Secrets From Getting Committed in the Future
Cleaning up after a leak is manageable. Preventing the next one is cheaper:
- Add
.envto.gitignorebefore writing any secrets into it, not after. - Use pre-commit hooks (
git-secrets,gitleaks) to block commits containing credential patterns before they ever reach a remote. - Store secrets in environment variable managers (Vercel env vars, AWS Secrets Manager, Doppler) instead of files that live next to your code.
- Audit regularly, not just once. A clean scan today doesn't mean a clean scan in three months, especially as more people commit to the repo.
For a broader look at what else to fix beyond secrets, the GitHub repo security checklist covers the rest of the pre-launch security surface, and GitHub secret scanning goes deeper into the specific patterns that give away exposed keys.
Scanning your repo once is a good start. Making it part of your regular workflow is what actually keeps you safe. The earlier you catch an exposed secret, the lower the blast radius.