Your API keys might be in the browser. Here is how to check in 60 seconds
Open your live site, press F12 to open developer tools, go to the Sources tab and search the loaded JavaScript for "sk_", "secret" and "service_role". Anything that matches is readable by every visitor. It must be rotated, not just moved, because it has already been published.
Part of the guide to shipping an app built with an AI coding tool.
Why this happens
Anything your front-end code can read, a visitor can read. There is no exception to this: the JavaScript that runs in someone's browser was downloaded by that browser, and they can read all of it. Minifying it changes nothing — search still works.
Keys end up there because it is the path of least resistance. A feature needs to call an API, the key goes in the front-end because that is where the code is, and it works immediately. Nothing fails, so nothing signals a problem.
The check itself
- Open your live site in Chrome or Edge.
- Press F12 to open developer tools.
- Go to the Sources tab.
- Press Ctrl+Shift+F to search across all loaded files.
- Search for: sk_ — then secret — then service_role — then api_key.
Any match is public. Not "potentially exposed" — published, to everyone who has ever loaded your site.
Which keys are safe to have there
| Key | Safe in the browser? | Why |
|---|---|---|
| Supabase anon key | Yes, by design | Only grants what your RLS policies allow. Safe if those policies exist. |
| Supabase service_role key | Never | Bypasses every RLS policy. Full read and write to everything. |
| Stripe publishable key (pk_) | Yes, by design | Intended for the front-end. Cannot move money on its own. |
| Stripe secret key (sk_) | Never | Can charge cards, issue refunds and read your customer list. |
| Firebase config | Yes, by design | Public identifiers. Safety comes from your security rules. |
| OpenAI / Anthropic key | Never | Anyone can spend your quota on their own workload. |
| Email provider key | Never | Anyone can send mail as you, which loses you your sending reputation. |
You found one. Now what?
Rotate it first, before anything else. Moving the key server-side does not help — the old value is already out, sitting in browser caches and possibly in your public repository history. Generate a new key and revoke the old one.
- Generate a replacement key in the provider's dashboard.
- Revoke the old one. Do not skip this because you are worried about downtime — an exposed key is worse than a brief outage.
- Move the call that needed it to the server: an API route, an edge function or a serverless function. The browser calls your server; your server holds the key.
- Check your billing and usage history for the period the key was public.
- If the key was ever committed, remember that repository history keeps it even after you delete the line.
The second check worth doing
While the developer tools are open, sign up as a second user and try to reach the first user's data by changing an ID in the URL. That single test finds more real problems than any automated scan, and it costs you two minutes.
Want someone to look at yours?
Send us the repo or the live URL. You get a written list of what is broken, what each fix costs and what can wait — back within 24 hours, and yours to keep either way.
Get a $99 audit