Why is my API key exposed?
A key is exposed when it is readable by anyone: bundled into browser JavaScript, committed to a repository, or returned by an endpoint. Rotation comes first, then the code fix, because the old value is already copied and cached wherever it appeared.
The problem
A secret key appears somewhere the public can read it. You may have found it yourself, or received an automated warning from the provider or a repository scanner.
What it means
Anyone can now act as your application against that service — spending quota, sending mail, reading data — until the key is revoked. Publishable keys are a different matter: they are meant to be visible and are not an incident.
Common causes
- A secret used in a component, which ships to the browser.
- A public prefix added to a secret so that a component could use it.
- A configuration file with real values committed to the repository.
- A debug endpoint or error response that echoes configuration.
- A screenshot or a pasted snippet in a public place.
How to diagnose it
- 1Open the live site's bundled JavaScript and search for the key's first characters.
- 2Search the whole repository history, not just current files.
- 3Check any endpoint that returns configuration or verbose errors.
- 4Review the provider's usage log for requests you did not make.
rg -n "sk_live|service_role|BEGIN PRIVATE KEY" --hiddenHow to fix it
- 1Rotate the key at the provider immediately. This is the only action that stops the exposure.
- 2Add the new key to your server environment only.
- 3Move the code that uses it into a server function.
- 4Redeploy and confirm the new bundle contains no key.
- 5Review the provider's log for unauthorised use while the old key was live.
What AI may have done
Written the integration in the place where the data was needed — a component — because that produces working code fastest. It may also have added a public prefix when the build tool refused to supply the value, which is the exact moment a secret becomes public.
How to verify the fix
- 1Search the newly deployed bundle for the old and new keys; both should be absent.
- 2Confirm the feature still works, now going through your own endpoint.
- 3Confirm the old key is revoked by calling the provider with it and receiving a rejection.
- 4Check that repository scanning is enabled so the next one is caught automatically.
How to prevent it
- Never prefix a secret to make it available in the browser.
- Keep all secret use inside server functions.
- Ignore environment files in version control from the first commit.
- Review any AI change that adds a new integration.
- Scope keys to the minimum permissions the provider offers.
Frequently asked questions
- Is deleting the commit enough?
- No. Rewriting history is unreliable and the value may already be cached or cloned. Rotate.
- My anon key is visible — is that a problem?
- No, it is designed to be. Confirm your database access rules are doing the protecting.
- How would I know if it was used?
- The provider's usage and audit logs. Look for unfamiliar times, volumes or regions.
Further reading and lessons
Try the linked activities for troubleshooting and check your answer against the exercise feedback.
Related troubleshooting guides
- How to keep API keys out of your frontendA practical method for moving secret-using code to the server, checking what actually shipped to the browser, and proving a key is no longer exposed.
- API keys explainedWhat an API key is, what it proves, why some keys are safe in a browser and others are not, and how to handle one without leaking it.
- Common security mistakes in AI-generated appsThe handful of issues that show up repeatedly in generated code — what each one looks like, why it happens, and the check that catches it.
- How to debug an AI-built appA repeatable method: reproduce it, read the real error, find the layer, change one thing, verify. Written for apps whose code you did not type.
- How to troubleshoot an AI-built applicationA symptom-to-cause index. Start from what you can see — a blank page, an empty list, a failed login, a dead deploy — and follow it to the layer responsible.