I committed my .env or API key to GitHub: what to do now
By Obscura Security · Updated · GitHub, Git, Lovable, Bolt, Cursor
Short answer
Treat any key that has been pushed to GitHub as stolen, even if you deleted the file straight away, because git history keeps it and scanners copy public commits. Rotate the key first, then remove it from history with git filter-repo, add .env to .gitignore, and turn on GitHub secret scanning and push protection.
If a .env file, a Stripe secret key, an OpenAI key or a database password has been pushed to GitHub, assume someone has it. Deleting the file doesn’t undo that.
Why this happens
AI tools and quick-start templates create .env files all the time, and a git add . sweeps them into a commit. Sometimes the key is pasted straight into a code file because it’s the quickest way to get something working. Sometimes the repo was private, and was later made public with its whole history.
Git stores every version of every file. When you delete a file in a new commit, the old commit that contains it is still there, and so is the key. Anyone who can view the repository can step back to that commit.
Automated scanners watch public GitHub activity continuously and collect anything that looks like a key. So if the repository was public at any point, treat the key as copied, even if you removed it within seconds.
How to check your own app
- GitHub’s own alerts. In your repository, open the Security tab and look at secret scanning alerts. Also check your email for messages from GitHub or from the key’s provider about an exposed secret.
- Search your history for a specific key or file. Run these in your own clone:
# Commits that added or removed a specific string
git log --all -p -S 'sk_live_'
# Every commit that touched a .env file, on any branch
git log --all --full-history -- .env '.env.*'
- Scan the whole history with a secret scanner. Both of these are free and run on your own machine:
gitleaks detect --source .
trufflehog git file://. --only-verified
Newer gitleaks releases also have gitleaks git, which does the same history scan. TruffleHog’s --only-verified flag only reports secrets it has confirmed are still live, so run it without that flag too if you want to see everything that looks like a key.
- Look at your provider’s usage logs. In the dashboard for Stripe, OpenAI, Supabase, AWS or whichever service issued the key, look for requests you don’t recognise since the date of the commit.
The fix
1. Rotate the key first
Do this before anything else, because cleaning history doesn’t help if the key still works. Create a new key in the provider’s dashboard, put the new one in your host’s environment variable settings, redeploy, then revoke the old one. Common places:
- Stripe: roll the secret key in the Developers section, and roll any webhook signing secret that was in the file.
- OpenAI and other AI providers: create a new API key and delete the old one.
- Supabase: if the service role key or the database password was in the repo, reset them in the project settings. See Supabase service role key exposed.
- Database URLs, JWT secrets, session secrets: change the password or secret and redeploy. Changing a session secret will log everyone out.
Then read the provider’s logs for the period the key was exposed. If you see use you don’t recognise, treat it as a breach of that service and check what data it could reach.
2. Stop it happening again
Add the file to .gitignore:
.env
.env.*
!.env.example
If the file is already tracked, .gitignore alone does nothing. Remove it from git’s index while keeping your local copy:
git rm --cached .env
git commit -m "Stop tracking .env"
Keep a .env.example with fake placeholder values so other people know which variables to set. In production, set the real values in your host’s environment variable settings, not in a file in the repository.
3. Remove the secret from history
Once the key is rotated, you can clean the history. Use a fresh clone, because git filter-repo refuses to run on one that isn’t, and it rewrites commits.
git clone https://github.com/YOUR-NAME/YOUR-REPO.git repo-clean
cd repo-clean
# Remove the file from every commit
git filter-repo --invert-paths --path .env
If the key was written inside code files, replace the text instead. Create expressions.txt containing a line like this (then delete the file, since it contains the key):
literal:THE-LEAKED-KEY-VALUE==>REMOVED
git filter-repo --replace-text expressions.txt
git filter-repo removes the origin remote as a safety step, so add it back and force-push:
git remote add origin https://github.com/YOUR-NAME/YOUR-REPO.git
git push origin --force --all
git push origin --force --tags
Everyone else who has cloned the repo needs to re-clone, or they will push the old history back. BFG Repo-Cleaner is an alternative; note that by default it leaves your latest commit untouched, so remove the file from the current version first.
GitHub may still hold the old commits in cached views, in forks, and in pull request references that you can’t rewrite. If the leak included personal data and not just keys, contact GitHub Support and ask them to remove the cached data. This is one more reason rotation comes first.
4. Turn on GitHub’s protection
In the repository, open Settings and find the code security section. Turn on secret scanning and push protection. Push protection blocks a push that contains a recognised secret before it reaches GitHub. Public repositories get secret scanning at no cost. For private repositories, what’s available depends on your plan, so check what your account offers.
You can also run gitleaks as a pre-commit hook on your own machine, so the secret never gets committed in the first place.
Mistakes to avoid
- Cleaning the history and skipping the rotation. The key may already have been copied, and a copy keeps working.
- Making the repo private and calling it done. Anyone who cloned it while it was public still has the key.
- Putting a real secret in a
NEXT_PUBLIC_orVITE_variable. Those get built into the frontend and shipped to every visitor. See secret API key in frontend. - Committing
.env.localor.env.productionbecause.gitignoreonly listed.env. Use the pattern.env.*. - Sharing the new key in chat or email and creating a second leak.
Common questions
- I deleted the .env file in a new commit. Am I safe?
- No. The earlier commit that contains the file is still in the repository history, and anyone who can see the repo can open it. Deleting the file only changes the latest version.
- My repo was private when I committed it. Do I still need to rotate?
- If it has stayed private and only people you trust have access, the risk is lower, but rotating is cheap. If the repo is ever made public, or a collaborator's account or laptop is compromised, the whole history goes with it, so rotate anyway.
- Do I need to clean the git history if I've rotated the key?
- Once the old key is revoked, it no longer works, so cleaning history matters less. Do it if the commits also hold other secrets or personal data you can't rotate, or if you want to stop scanners and alerts from flagging the old key.
- Which keys are fine to have in a public repository?
- Keys designed to be public, like a Supabase anon or publishable key, a Stripe publishable key, or a Firebase web config. Anything called secret, service role, private or admin is not, and neither are database URLs with passwords, JWT signing secrets and webhook secrets.
- Will GitHub tell me if I push a secret?
- GitHub scans for known secret formats and can alert you, and for some providers it notifies them so they can revoke the key. Coverage depends on the key type, and some features depend on whether the repository is public or private and on your plan. Don't rely on it as your only check.
Read next
Written by Obscura Security, an Australian practice that checks web apps for problems like this one. If you want a second pair of eyes on yours, email [email protected].