Everything in your front-end bundle is public. If a secret key is in it, anyone who opens the page can copy it and use it as you. With an AI provider key that means unlimited requests billed to your account.
Secret keys and public keys
Some keys are meant to be in the browser. Reporting them would be a false alarm, so SecScan does not.
| Public by design (not a leak) | Secret (a finding) |
|---|---|
Stripe pk_live_, pk_test_ | Stripe sk_live_, rk_live_ |
| Supabase anon or publishable key | Supabase service_role key |
Firebase AIza… web API key | Cloud or service-account private keys |
| Google Maps browser key (restrict by referrer) | OpenAI sk-…, Anthropic sk-ant-…, AWS secret access key, GitHub ghp_… |
A key that is public by design is never reported as a leaked secret. Some still get an
Info note, titled “found in client code” and not “exposed”, so you can check that the
key is restricted: a Firebase config block, a Google or Mapbox browser key, a Twilio key
SID on its own. Info notes carry no weight in the grade. An AWS access key ID on its own
(AKIA…) is a Low note, because it is an identifier and the secret half is what grants
access; the secret access key is Critical.
How it happens
- A build variable with a public prefix (
VITE_,NEXT_PUBLIC_,REACT_APP_) holds a secret. Anything with that prefix is compiled into the bundle. - An AI tool calls the provider straight from the browser because that is the shortest code.
- A key was pasted into a component while testing and never removed.
What to do
- Revoke the key now at the provider, then create a new one. Removing it from the code is not enough: it is already in the page you served and in any cache.
- Move the call to a server route (for example
/api/chat) that adds the key from a server-side environment variable. The browser talks only to your route. - Check the route is safe to expose: require sign-in and rate-limit it, or anyone can spend your credit through it.
- Rescan to confirm.
SecScan also flags the design even when no key is visible: an AI provider called directly from the browser, or a system prompt shipped in the bundle.
Last reviewed 2026-09-30