To keep API keys safe, keep them out of your code entirely. Store them in environment variables or a secret manager, add .env to .gitignore before your first commit, never ship a secret key to the browser, and let GitHub’s push protection block anything that slips through. If a key does leak, revoke it first and clean up second.
Coding with AI adds two new ways to leak: assistants that paste a key straight into a file, and agents that read your keys into their context. Good API key security covers both, and it takes an afternoon to set up.
- Treat a key that reaches a public repository as stolen, even if you delete it a minute later.
- Keep keys in environment variables or a secret manager, and ignore
.envfiles from the first commit. - Anything sent to a browser is public. Secret keys belong on a server.
- Give AI agents scoped, low-limit development keys, and never paste production keys into a chat.
- After a leak: revoke, check usage, replace, then clean up history. In that order.
How API keys actually leak
Most leaks are not hacks. They are copies: a key ends up somewhere it was never meant to be, and someone else reads it. GitGuardian monitors public GitHub commits, and its latest yearly report shows the scale.
The last number matters most. Leaked keys often stay live for years because nobody rotates them.
Here are the common paths, and the fix for each.
| Leak path | How it happens | The fix |
|---|---|---|
Committed .env file | The file was committed before anyone ignored it | Ignore it before the first commit |
| Hardcoded in source | You pasted a key into a chat and the assistant wrote it into a file | Give the agent the variable name, never the value |
| Browser bundle | The key sits in front-end code or a public build variable | Call the API from a server route |
| Logs and error output | Debug code prints headers, config or the whole environment | Log the key’s name, never its value |
| Agent context | An agent reads .env while debugging, and the key lands in the transcript | Block .env from the agent and use a limited dev key |
| Tickets, chats and screenshots | A key pasted into an issue, a team chat or a screen recording | Share a secret manager link instead |
AI tooling adds its own files to the list. The same report found 24,008 unique secrets in MCP configuration files, the files that connect agents to tools. It also found that commits made with Claude Code’s help leaked secrets at a 3.2% rate, against 1.5% for all public commits. That does not prove the tool caused the leaks. It does show that AI help alone does not keep keys out.
Why a deleted key is still a leaked key
Automated scanners copy public repositories around the clock. In 2023, Palo Alto Networks’ Unit 42 watched one campaign find exposed AWS keys on GitHub and start using them within five minutes, to run crypto-mining machines on the victim’s bill.
Deleting the commit does not undo that. Copies live on in clones, forks and caches, and GitHub’s own guide warns that you cannot remove data from other people’s clones or from forks. The only fix that works everywhere at once is making the key useless.
Five habits for API key security
Keep keys in the environment, not the code
Your code reads a key by name, and the value lives outside the repository: in a local
.envfile for development, and in your host’s secret settings for production. Ignore the file before your first commit, and commit an example file that lists names without values..gitignore .env .env.* !.env.exampleIf
.envwas committed before you ignored it, git keeps tracking it anyway. Rungit rm --cached .env, commit, and rotate every key the file ever held, because old commits still contain them.Never ship a secret key to the browser
Everything a browser downloads, anyone can read. Next.js inlines any variable that starts with
NEXT_PUBLIC_into the JavaScript it sends to visitors, and Vite does the same withVITE_. Vite’s docs say plainly that these variables should not hold API keys.The fix is a small server route that holds the key and calls the API for the page. Put a login check or a rate limit on that route too, or anyone can spend your credits through it.
Use scoped, short-lived keys
Create one key per project and per environment, so a leak stays small. Give each key the fewest permissions that work. On GitHub, a fine-grained personal access token can be limited to specific repositories and given an expiry date, and GitHub recommends it over the classic kind. Where your AI provider offers a spending limit or budget alert, turn it on.
Turn on push protection and a local scanner
Push protection blocks a push when it detects a secret, before the secret lands. It is on by default for your personal account when you push to public repositories. For private repositories, it needs GitHub Secret Protection, a paid add-on. A free local scanner is a good second net:
Terminal # Scan every commit in this repository for secrets gitleaks git -v .Gitleaks can also run as a pre-commit hook, so a commit containing a key never gets made. If push protection blocks you, do not bypass it out of habit: bypassing on a public repository publishes the key.
Keep keys out of your agent’s context
Anything a cloud-based agent reads goes to the model provider, and usually into a saved session transcript as well. What happens to it next depends on the provider. So tell the agent the variable name, not the value, and block it from reading
.envfiles. In Claude Code that is a deny rule in its settings file. Cursor ignores.envfiles by default..claude/settings.json { "permissions": { "deny": ["Read(./.env)", "Read(./.env.*)"] } }These rules are guardrails, not vaults. Claude Code’s docs say a script that opens files itself is not covered, and Cursor’s docs say the agent’s terminal and MCP tools can still reach ignored files. On a machine where an agent runs, keep only development keys with low limits. The same applies in CI, where an agent running in your pipeline needs its secrets scoped just as tightly.
Paste rules like these into your agent’s project instructions, so it follows them in every session:
Follow these rules for secrets in this project: 1. Never write an API key, token or password into a source file, test, log line or commit. 2. Read secrets from environment variables. If one is missing, stop and tell me its name. 3. Never open, print or copy .env files. Use .env.example to learn the variable names. 4. Never send a secret to the browser. Calls that need a secret key go through a server route. 5. If you see a secret in code or in command output, stop and tell me where it is.
For the wider question of what an agent can reach on your machine, see whether it is safe to let AI control your computer.
What to do in the first hour after a leak
Speed matters more than tidiness. Work through these in order, and do not start with git.
Revoke or rotate the key
Go to the provider’s dashboard and revoke the key, or rotate it if other systems depend on it. GitHub’s guide puts this first, because a dead key is harmless wherever it was copied.
Check what it was used for
Open the provider’s usage, billing and audit logs for the time the key was exposed. Look for requests, spend or new resources you do not recognize, such as unexpected cloud machines.
Put the new key where it belongs
Store the replacement in your host’s secret settings or your secret manager, redeploy, and confirm the app still works.
Clean up the code and, if needed, the history
Remove the key from the code. Rewriting history is optional once the key is dead, and it has costs: every later commit hash changes, and copies in forks and clones stay out of reach. If you do it, GitHub recommends git filter-repo, and GitHub Support can clear cached views.
Close the gap and tell people
Find the path the key took and fix it with the habits above. If the key could reach a client’s or user’s data, tell them what happened.
A checklist for every project
AI-written code has other weak spots besides keys. Our checklist for securing AI-generated code covers the rest.
FAQ
Is it safe to keep API keys in a .env file?
For development, yes, as long as the file is in .gitignore. It is plain text on your disk, so it prevents accidental commits, not theft by anything that can read your files. Production keys belong in your host’s secret settings or a secret manager.
Can I hide an API key in front-end code?
No. Minifying, encoding or splitting the key does not help, because the browser needs the real value to use it. Put the call behind a server route. Some keys are designed to be public, such as Stripe’s publishable key, and the provider’s docs say which ones.
Does GitHub scan private repositories for secrets?
Only with GitHub Secret Protection, a paid add-on for GitHub Team and Enterprise Cloud. Public repositories are scanned for free, automatically. Organizations can also run a free report that scans their code for leaked secrets.
Should I give my AI coding agent my API keys?
Give it only what it needs to run the project locally: a development key with low limits, read from an environment variable. Never paste a production key into a chat, where it becomes part of the conversation history.
- Keys belong in environment variables or a secret manager, never in code or in the browser.
- Scoped keys with limits turn a leak from a disaster into a chore.
- Push protection and a local scanner catch what habits miss.
- Give agents variable names and development keys, and block them from
.envfiles. - After a leak, revoke first. Cleaning up history comes last, if at all.
Read next: how to secure the code your agent writes, and how prompt injection can turn an agent against you.
- The State of Secrets Sprawl 2026, GitGuardian, March 2026
- CloudKeys in the air: tracking malicious operations of exposed IAM keys, Palo Alto Networks Unit 42, October 2023
- Push protection, GitHub Docs
- Secret scanning, GitHub Docs
- Removing sensitive data from a repository, GitHub Docs
- Managing your personal access tokens, GitHub Docs
- How to use environment variables in Next.js, Next.js, August 2026
- Env variables and modes, Vite
- Configure permissions, Claude Code Docs
- Ignore file, Cursor Docs
- Gitleaks, Gitleaks on GitHub




