A key is a password your app uses to talk to another company's service — OpenAI for AI features, Stripe for payments, a database for your records. Whoever holds the key can use it. There is no second check.
The problem is where the key ends up. Code that runs on your server is private: nobody outside can read it. Code that runs in your visitor's browser is not — it is downloaded, in full, by everyone who opens the page, including anyone who wants to go looking. AI coding tools are fast and confident, and they will happily put a key in the second place because the app works either way. It works right up until someone finds it.
The bill is the usual way people find out. Someone's OpenAI key gets picked up and used by strangers, and the first sign is an invoice that doesn't match the traffic.
The thirty-second check
- 1Open your live site in Chrome, Edge or Firefox.
- 2Press Ctrl+U (Cmd+Option+U on a Mac) to view the page source, or open developer tools and pick the Sources tab.
- 3Search the page with Ctrl+F for: sk-, AKIA, sk_live, SUPABASE_SERVICE, or the word secret.
- 4Repeat the search inside the .js files the page loads — that's where the app's real code lives, and where a key is far more likely to be sitting.
A hit on any of those is worth treating as real. Those prefixes belong to secret keys: sk- is OpenAI, AKIA is Amazon Web Services, sk_live is a live Stripe key.
One thing that looks bad and isn't
Not every key is a secret. Some are designed to be public, and finding one is fine:
- pk_live_… and pk_test_… — Stripe publishable keys. Meant for the browser. Safe.
- A Supabase anon key — meant for the browser, but only safe if your row-level security rules are switched on. Worth checking separately.
- sk_live_… — a Stripe secret key. Never safe in a browser.
- SUPABASE_SERVICE_ROLE_KEY — bypasses every rule you've set. Never safe in a browser.
The difference between pk_live_ and sk_live_ is two characters and the whole of your Stripe account.
If you find one
- 1Revoke it first, in the provider's dashboard. Don't wait until the code is fixed — a key you've deleted can't be used, and a key you've merely stopped using can.
- 2Issue a new one and put it somewhere only your server can read: an environment variable on your host, not a file in your code.
- 3Move the call. Anything using that key has to run on your server, with your app asking your server rather than asking OpenAI or Stripe directly.
- 4Check your billing and usage history for the period the key was exposed.
- 5Check your version history. A key that was committed to Git is still in the history even after you delete the line.
Point four is the one people skip. Rotating the key stops the bleeding; it doesn't tell you whether anything already happened.
Why this keeps happening
It isn't carelessness, and it isn't unique to any one tool. An AI coding assistant is optimising for code that runs. Putting the key in the browser produces code that runs. Nothing in the loop pushes back, because the app looks correct from the outside — and it is correct, in the only sense the tool was asked about.
This is the single most common thing we find, and it's also the easiest to check yourself. If the search above turns up nothing, that's genuinely good news for that one class of problem — and it says nothing about your database rules, your sign-in, or anything else that lives on the server side.