GUIDESEPISODES
Deleted your API key? It still works. Revoke it first
You pasted a key into your code, pushed, then deleted the line. The key still works. Every old commit still holds it, and so does every clone and fork. Deleting the line buries it. Revoking it is the only thing that kills it. Here is the order that works, from GitHub’s own docs.

01THE PROBLEM
Why deleting the line doesn’t kill the key
Why it mattersDeleting the line buries the key. Only the provider can kill it.
Git doesn’t save just your latest code. It saves every version, forever. Each commit is a snapshot. When you delete a key in a new commit, the old snapshot still has it.

Open the full chapterIncludes code, a note
# Monday: the key goes in
git commit -m "add chat" # app/chat.ts holds sk-proj-...
# Tuesday: you delete the line
git commit -m "remove key" # app/chat.ts is clean now
# Anyone with a copy of the repo, any day after:
git show HEAD~1:app/chat.ts # the key is right theregit show prints a file as it was in any old commit. Anyone who cloned or forked the repo can run it.
If the repo is public, assume someone already copied the key. Even in a private repo, everyone with access has every old commit.
GitHub’s docs list where the data can still live after you clean up: other people’s clones and forks, cached views that open by commit hash, and pull requests built on the old history.
02STEP 1
Revoke or rotate the key first
Why it mattersRevoke first, and every copy of the old key dies at once.
GitHub’s docs put this first, before any cleanup: “as a first step you need to revoke and/or rotate that secret.” They add: “Once the secret is revoked or rotated, it can no longer be used for access.”

Open the full chapterIncludes steps, a table, a note
Rotate means: make a new key, switch your app to it, then delete the old one. Do it in this order so your app doesn’t go down in between.
- In the provider’s dashboard, create a new key.
- Put the new key in your server’s environment variables (your host’s settings, and your local
.env). Redeploy. - Check the app still works.
- Delete or roll the old key in the provider’s dashboard. From now on it’s dead, in every commit, clone and fork.
| Key | What it looks like | Where to kill it |
|---|---|---|
| OpenAI | sk-proj-… or sk-… | OpenAI platform → API keys: create a new key, delete the old one |
| Stripe secret / restricted | sk_live_… / rk_live_… | Stripe Dashboard → Developers → API keys: roll the key |
| Supabase | sb_secret_… or the service_role key | Project Settings → API Keys: create a new secret key, delete the leaked one |
| GitHub token | ghp_… / github_pat_… | GitHub Settings → Developer settings → Personal access tokens: delete it |
| AWS access key | AKIA… | IAM → your user → Security credentials: deactivate, then delete |
Menu names change. If you can’t find it, search the provider’s docs for “rotate API key”.
Then, if you want: clean the history
Rewriting history removes the key from your repo’s old commits. GitHub recommends git-filter-repo (a version with the --sensitive-data-removal flag, at least 2.47). It’s optional, and it has side effects. Every commit after the key gets a new hash, clones and forks keep the old copy, and teammates must re-clone.
03STEP 2
Keep the new key out of your code
Why it mattersA secret in .env, on the server, never prefixed, never committed.
The new key needs one home: an environment variable that only your server reads. Three rules keep it there.

Open the full chapterIncludes code, a note
Rule 1: no public prefix on a secret
Next.js puts any variable that starts with NEXT_PUBLIC_ into the JavaScript every visitor downloads. Vite does the same with VITE_. That prefix is for public values, never for secret keys.
# Shipped to every visitor's browser
NEXT_PUBLIC_OPENAI_API_KEY=sk-proj-...
VITE_STRIPE_SECRET_KEY=sk_live_...# Server only: no public prefix, and this file is never committed
OPENAI_API_KEY=sk-proj-...your-NEW-key
STRIPE_SECRET_KEY=sk_live_...your-NEW-keyRule 2: .env never gets committed
.env
.env.*
!.env.exampleCommit a .env.example with the variable names and no values, so others know what to set.
# .gitignore only stops NEW files. If .env is already tracked:
git rm --cached .env
git commit -m "Stop tracking .env"
# Your local .env stays on disk. The old commits still hold the old key,
# which is why you revoke it first.Rule 3: the key is used on the server only
The browser calls your own route. Your route calls the provider with the key. Here is a Next.js route handler:
import OpenAI from 'openai';
// Runs on the server only. The browser never sees the key.
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function POST(req: Request) {
const { message } = await req.json();
const res = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{ role: 'user', content: message }],
});
return Response.json({ text: res.choices[0].message.content });
}Add a login check and a rate limit before you ship it. Our AI bill guide shows how.
04STEP 3
Block the next leak with push protection
Why it mattersStop the key at the gate, and there’s nothing to clean up.
GitHub secret scanning has a gate called push protection. When it spots a supported secret in a push, it blocks the push before the key reaches GitHub.

Open the full chapterIncludes a note
In February 2024, GitHub began turning it on for all pushes to public repositories. When it fires, you can remove the secret from your commits, or bypass the block.
- Public repo: check it’s on in your repo’s Settings, in the security section.
- Private repo: push protection needs a plan that includes GitHub’s secret scanning for private repositories. Turn it on where your plan allows.
- On your own machine: a pre-commit hook with gitleaks stops a key before it ever becomes a commit.
05STEP 4
Search your history for every other key
Why it mattersEvery key in history is live until you revoke it.
One leaked key usually has company. Search every commit on every branch, not just today’s files. These commands only read.

Open the full chapterIncludes code, a note
# 1. Did a .env file ever get committed, on any branch?
git log --all --oneline -- .env '.env.*'
# 2. Which commits ever added or removed a known key prefix?
git log --all --oneline -S 'sk_live_'
git log --all --oneline -S 'sk-proj-'
# 3. Every line in history that looks like a key (prints matches: keep the output private)
git log --all -p | grep -oE 'sk-(proj-)?[A-Za-z0-9_-]{20,}|sk_live_[A-Za-z0-9]{10,}|rk_live_[A-Za-z0-9]{10,}|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{36}|sb_secret_[A-Za-z0-9_-]{10,}' | sort -u | cut -c1-12The last command cuts each match to 12 characters, enough to tell which key it is. Never paste full keys into a chat, issue or screenshot.
Dedicated scanners know hundreds of key formats. gitleaks and TruffleHog both scan full git history. TruffleHog can also check whether a key it finds still works.
The easier way: paste the prompt at the top of this page into Claude Code, Cursor or any agent. It searches the code and the history, masks every key, and tells you where to revoke each one.
06WAS IT USED?
How to tell if someone used your leaked key
Why it mattersRevoke stops the next charge. The usage page shows the last ones.
Revoking stops new use. It doesn’t tell you what already happened. Check the provider’s own records for the days the key was exposed.

Open the full chapterIncludes a table, a note
| Where | What to look for |
|---|---|
| Usage or billing page | A spike on days you didn’t ship anything, or usage at hours you were asleep |
| Per-key usage (where shown) | Traffic on the old key after you stopped using it |
| Logs (Stripe, Supabase, AWS) | Requests from IP addresses or regions you don’t know |
| Your data | Rows changed or exported that your app didn’t touch |
07WHY CLAUDE
Why this is Claude’s episode
Why it mattersFast commits need a gate. Revoke, hide, block, search.
GitGuardian’s State of Secrets Sprawl 2026 found that “Claude Code co-authored commits leak secrets at ~2× the baseline across all Public GitHub commits.” Its DEV summary puts numbers on it: a 3.2% secret-leak rate, versus a 1.5% baseline.

Open the full chapterIncludes a table, a note
That isn’t Claude going rogue. The same summary says “Developers remain in control of what gets accepted, edited, ignored, or pushed.” Agents write code fast, and you approve it fast. A key pasted into a prompt can end up in a file, and then in a commit.
| Finding (GitGuardian, 2026 report) | Number |
|---|---|
| New secrets in public GitHub commits in 2025 | 28,649,024 (+34%) |
| AI-service secrets leaked | 1,275,105 (+81%) |
| Valid secrets from 2022 still exploitable when retested in January 2026 | 64% |
THE QUICK READ
What to take away.
In one minute.
- 01
Deleting a key from your code doesn’t stop it. Every old commit still holds it, and anyone with a copy of the repo can open one.
- 02
GitHub’s docs: “as a first step you need to revoke and/or rotate that secret.” Once it’s revoked, “it can no longer be used for access.”
- 03
Rewriting history comes after, and it’s optional. Copies can survive in clones, forks and cached views.
- 04
The new key goes in
.env, in.gitignore, on the server only. ANEXT_PUBLIC_orVITE_name ships it to the browser. - 05
GitHub push protection blocks known secret formats on pushes to public repositories. It can be bypassed, so keep the other steps.
- 06
GitGuardian found 64% of valid secrets detected in 2022 still exploitable when retested in January 2026. Leaked keys don’t die on their own.
THE FIX, IN ONE PASTE
Paste this. Find every key your history still holds.
Paste this into Claude Code, Cursor, Codex, Gemini CLI or Copilot. It only reads and reports; it won’t change your code until you approve.
You are a security reviewer for this codebase. Investigate ONE issue: secret keys that are in the code now, or were ever committed to git history, even if a later commit deleted them. Where to look: - Current files: `.env*`, config files, `app/`, `src/`, `pages/`, `lib/`, `supabase/`, scripts. Flag secret keys behind `NEXT_PUBLIC_` or `VITE_` names. - Git history on every branch: search `git log --all -p` for key patterns (`sk-`, `sk_live_`, `rk_live_`, `AKIA`, `ghp_`, `sb_secret_`, `service_role`) and run `git log --all -- .env` for committed env files. Use gitleaks or trufflehog if installed. - `.gitignore`: does it cover `.env`? Is any `.env` file still tracked? How to judge: critical = a secret key in history or in current code; high = a secret in client code or a tracked `.env`; low = a publishable or anon key. Report a table: file:line or commit | provider | masked key (first 4 characters only) | severity | exactly where to revoke or rotate it. Then list the fix in order: revoke or rotate each key first, put the new key in `.env` (server-side only, listed in `.gitignore`), and turn on GitHub push protection. Rules: read and report only. Don't modify any file or rewrite history until I approve. Never print a full key: mask it. Then ask me which fixes to apply.
Paste it into the agent you already build with
- Claude
- ChatGPT
- Gemini
- Cursor
- Copilot
QUESTIONS PEOPLE ASK
Good questions.
Short answers.
I deleted the API key from my code. Is it safe now?
No. Every old commit still holds the key, and anyone with a copy of the repo can read it. Revoke or rotate it in the provider’s dashboard. A revoked key stops working everywhere.
Should I rewrite git history to remove the key?
Revoke first. GitHub’s docs call revoking or rotating the first step and say it may be enough on its own. Rewriting history with git-filter-repo is optional. It changes commit hashes, and copies survive in clones, forks and cached views.
What is the difference between revoking and rotating a key?
Revoking kills the old key. Rotating makes a new key, switches your app to it, then kills the old one. Rotating keeps your app running.
Does adding .env to .gitignore remove it from GitHub?
No. .gitignore only stops untracked files. If .env was committed, run git rm --cached .env and commit, then revoke every key that was in it.
Is GitHub push protection on for my repo?
GitHub turned it on for pushes to public repositories in February 2024. Private repositories need a plan that includes secret scanning for private repos. Check your repo’s security settings.
How do I know if someone used my leaked key?
Check the provider’s usage or billing page and logs for the days the key was exposed. Look for spikes, odd hours and unknown IP addresses. Contact the provider’s support about charges you didn’t make.
Do AI coding agents leak more secrets?
GitGuardian’s 2026 report found Claude Code co-authored commits leak secrets at about twice the baseline: 3.2% versus 1.5%. It also says developers still decide what gets pushed.
Does Aster find keys in my git history?
No. Aster’s audit reads your repository’s current code. It flags secret keys committed in the code, a committed .env file and a missing .gitignore. It doesn’t scan old commits and can’t tell whether a key was revoked. Use the prompt above for the history.
A deleted key isn’t a dead key. Revoke it, keep the new one in .env on the server, turn on push protection, and search your history for the rest. Then get a second look at today’s code: your first Aster repository audit is free, and it flags keys committed in your code, a committed .env file and a missing .gitignore.Source notebook6 links
- GitHub Docs: Removing sensitive data from a repository
- GitHub blog (Feb 29, 2024): Keeping secrets out of public repositories
- gitleaks (open-source secret scanner)
- TruffleHog (open-source secret scanner)
- GitGuardian: The State of Secrets Sprawl 2026
- GitGuardian on DEV: The State of Secrets Sprawl 2026 (AI-service leaks surge 81%)

