My API keys are in the frontend. How bad is that?
Short answer: it depends which key it is. How to tell in two minutes whether yours is harmless or needs replacing tonight.
Someone looks over your code, or you finally read through what your AI tool put there, and there it is: your key. Plain, readable, in a file that ships to the browser. The first reaction is panic. Sometimes that is justified and sometimes it is not, and the difference can be worked out exactly.
Two kinds of keys
One kind is meant to be public. Your publishable Supabase key, your publishable Stripe key, your Google Maps key: they sit in every site that uses them and that is not a mistake. They only say who you are, not what you may do. What you may do is decided somewhere else.
The other kind must never reach the browser. Those are the keys that skip every rule: your database service key, your secret payment key, your OpenAI or Anthropic key, your mail key. Whoever holds one of those can do everything you can.
How to tell which one you have
- Does the word service, secret, private or admin appear in the name or in the key itself? Then it is the dangerous kind.
- A Stripe key starting with sk_ is secret. One starting with pk_ is not.
- In many frameworks the prefix of the environment variable decides whether it ships to the browser. If PUBLIC is in the name, it ships, always.
- Not sure? Open your live site, press F12, Sources tab, and search for the first characters of your key. If you find it, it is public.
The public key is not your security
This is where AI-built apps usually go wrong for real. The public database key is fine to share, but only if the rules on your tables are right. If your app does the checking in the screen, with a hidden button or a page you cannot reach without logging in, that is a curtain, not a lock. Whoever has the key talks to your database directly and skips your entire front end.
A public key is not a leak. A public key on a database without rules is.
What to do if it is the wrong kind
Replace it, in this order: create a new one, fill it in everywhere it is needed, and only then revoke the old one. After that, assume the old key has been used, even if you see nothing odd. Check the service's logs for what has been done with it over the past weeks.
And get it out of your git history, or accept that it sits there and revoke it for real. A key from an old commit works exactly as well as a key from the current one.
How to fix it structurally
Anything that has to stay secret moves to a piece of code that runs on the server: a route or an edge function. Your browser asks it for something, that server talks to the service and returns only the answer. That is half an hour of work per service and it is not complicated, only easy to skip when you wanted something working quickly.
If you are not sure what is exposed in your project, that is exactly what the vibecode scan looks at. You get back what is there and what has to happen first.