Guides / Exposed API keys

Is your OpenAI API key exposed in your frontend?

AI coding tools often wire API calls straight from the browser because it’s the fastest way to make a demo work. That puts your secret key in front of every visitor. Here’s how to check in five minutes and fix it properly.

Get your app checked by hand

How keys end up in the browser

Anything your frontend code can use, your visitors can read. Putting a key in a .env file doesn’t hide it if your build tool copies it into the code it sends to the browser, and the common frameworks do exactly that for certain variable names:

  • Vite (used by Lovable, Bolt and many React apps) exposes any variable starting with VITE_ to client code.
  • Next.js inlines any variable starting with NEXT_PUBLIC_ into the browser bundle.

So VITE_OPENAI_API_KEY or NEXT_PUBLIC_OPENAI_KEY is a public key, whatever the file it lives in.

Some keys are designed to be public: Stripe’s publishable key (pk_…) and Supabase’s publishable or anon key. Secret keys are not: OpenAI and Anthropic API keys, Stripe secret keys (sk_live_…), and Supabase secret or service_role keys. If one of those reaches a browser, someone can run up your AI bill, burn through your quota or, with a payment or database key, reach your customers’ data.

How to check (five minutes)

  1. Watch the network. Open your app, open dev tools (F12) and go to the Network tab. Use your AI feature. If you see requests going straight from the browser to api.openai.com, api.anthropic.com or a similar provider, your key is in the browser. Click the request and look at the Authorization or x-api-key header: that’s your key, readable by anyone.
  2. Search the code your site ships. In dev tools, open Sources and press Ctrl+Shift+F (Cmd+Option+F on a Mac). Search for sk-, sk_live, sb_secret and service_role. Any match in your app’s own files needs fixing.
  3. Check browser storage. Under Application → Local Storage, make sure no API keys are stored there. (Your session token being there is normal; a provider key is not.)
  4. Check your repository. Make sure .env is listed in .gitignore and was never committed. If your repo is on GitHub, check the Security tab for secret-scanning alerts.

For a worked example, the lab notes show API keys exposed in browser storage and what an attacker can do with them.

How to fix it properly

  1. Move the call to a server. Your browser should call your backend, and your backend calls the AI provider. On Supabase that’s an Edge Function; on Next.js, a route handler; elsewhere, any serverless function. Store the key as a server-side secret with no VITE_ or NEXT_PUBLIC_ prefix.
  2. Lock down the new endpoint. This step is what most fixes miss. If your new endpoint answers anyone, you’ve just built a free proxy for your API key. Require a signed-in user, limit how many requests each user can make, cap the input size, and fix the model and settings on the server rather than accepting them from the browser.
  3. Rotate the key. Treat any key that was ever in the browser or a public repo as compromised, even if you fixed the code. Create a new key in the provider’s dashboard, put it on the server, then delete the old one. See what to do when a key leaks for step-by-step instructions per provider.
  4. Set spending limits. In your AI provider’s billing settings, set a monthly budget or usage limit and an email alert, so a leak becomes an alert rather than a surprise invoice.

A fix prompt you can paste into your AI tool

Review the change before you ship it, then repeat the five-minute check above to confirm no key reaches the browser.

My app calls the OpenAI API directly from the browser, which exposes my
API key. Fix this:
1. Create a server-side function (Supabase Edge Function / Next.js route
   handler) that makes the OpenAI call. Read the key from a server-side
   secret with no VITE_ or NEXT_PUBLIC_ prefix.
2. The function must reject requests without a valid signed-in user
   session, limit each user to a reasonable number of requests per
   minute, cap the prompt length, and set the model on the server.
3. Change the frontend to call this function instead of OpenAI.
4. Remove every API key from frontend code and client-side env vars.
Then list every place a secret key appeared so I can rotate it.

The short version

  • No browser requests go directly to an AI or payment provider with a secret key.
  • No sk-, sk_live, sb_secret or service_role in your shipped code.
  • Secret keys live only in server-side env vars, with no public prefix.
  • The server endpoint requires login, rate limits users and fixes the model.
  • Any key that was ever exposed has been rotated.
  • A spending limit and alert are set with your AI provider.

The Launch Check covers exposed keys alongside your database rules, unprotected routes and AI features, with fixes you can paste into your coding tool.

Want a second pair of eyes?

Get your app checked before launch.

The Launch Check covers your database rules, exposed keys, unprotected routes and AI features by hand, with plain-English findings, fixes you can paste into your AI coding tool, and a recheck. $400, fixed price.