ShipWatchFree scan

OpenAI API key exposed in your frontend? Revoke, move, and check

An OpenAI key in your browser code is a prepaid card taped to your front door: anyone can copy it and bill requests to you. This page walks through killing the key, finding where it shipped, and moving the calls somewhere safe.

Short answer: Delete the key on the OpenAI API keys page first, then check usage for requests you didn't make. Move every OpenAI call into a server function that reads the key from a server-only environment variable, and confirm the old sk-proj- string is gone from your live JavaScript.

This guide is for one specific emergency: an OpenAI key that shipped to the browser. If you're not sure which provider leaked, start with the leaked API key checker. For the general pattern of moving any key out of client code, see API key exposed in your frontend.

Why this one hurts fast

OpenAI keys are pay-per-use. Whoever holds yours can run requests on your account until you notice the bill or hit your limits. Keys have a recognizable shape (modern project keys start with sk-proj-, and most OpenAI keys contain the string T3BlbkFJ), so automated scrapers that download JavaScript from public sites can pick them out with a simple pattern match. You should assume a key that was live in your bundle for any length of time has been copied.

The usual cause in AI-built apps: you asked for a chatbot or "summarize this" button, and the generated code imported the OpenAI SDK into a React component and read the key from import.meta.env.VITE_OPENAI_API_KEY. The official Node SDK even refuses to run in a browser unless you set dangerouslyAllowBrowser: true. If you see that flag in your code, you've found the leak.

Find it in DevTools [manual]

  1. Open your deployed site, not the editor preview.
  2. Open DevTools (F12 or Cmd+Option+I), go to Network, filter to JS, and reload.
  3. Press Ctrl+Shift+F (Cmd+Option+F on a Mac) to search all loaded sources. Search for sk-proj-, then T3BlbkFJ, then dangerouslyAllowBrowser.
  4. Also search for api.openai.com. A request from the browser straight to that host means the key is in the browser, even if the variable name hides it.
  5. Use the feature that calls OpenAI (send a chat message, click generate). Then repeat the search, because some apps load that code only on demand.

Fix it, in this order

  1. Revoke the key [manual]. Go to platform.openai.com, open API keys, and delete the leaked key. Revoking is instant and is the only step that stops someone who already copied it. Don't wait until the code fix is ready.
  2. Check usage [manual]. Open the Usage page and look for spikes, models you don't use, or activity at odd hours since the key first shipped. If you see charges you didn't make, contact OpenAI support with the dates.
  3. Cap the damage next time [manual]. In your organization or project limits, set a monthly budget and alert threshold so a future leak is noticed early. Give the app its own project and key so you can revoke it without breaking anything else.
  4. Move the call to a server. The browser should call your endpoint, such as /api/chat, and only that endpoint talks to OpenAI. On a Vite + Supabase stack that's usually a Supabase Edge Function. On Next.js it's a Route Handler. On Cloudflare it's a Worker. The endpoint should:
    • read the key from a server-only secret (for example Deno.env.get("OPENAI_API_KEY") in an Edge Function),
    • require a logged-in user, so strangers can't use your endpoint as a free proxy,
    • enforce a per-user rate limit and a maximum input length, and
    • pick the model on the server, not from a value the client sends.
    Remove every direct OpenAI call from client-side code, including any use of dangerouslyAllowBrowser and any VITE_ or NEXT_PUBLIC_ variable holding an OpenAI key. Create a server-side function named "ai-proxy" that reads OPENAI_API_KEY from a server-only secret, requires an authenticated user, limits each user to 20 requests per minute, rejects inputs over 4,000 characters, and hard-codes the model. Make the frontend call this function instead.
  5. Create a new key and store it server-side only [manual]. For a Supabase Edge Function: supabase secrets set OPENAI_API_KEY=sk-proj-..., or add it in the dashboard under Edge Functions, Secrets. Never put it back in a .env variable with a VITE_ or NEXT_PUBLIC_ prefix.
  6. Redeploy and search again. Repeat the DevTools search. The old key should be gone, there should be no new key, and no browser request should go to api.openai.com.

Make it stick

What the free scan checks here (and what it doesn't)

It fetches your live page and up to 25 scripts it links to and looks for OpenAI keys (sk-proj- and the T3BlbkFJ pattern), Stripe sk_live_/rk_live_, Anthropic sk-ant-, AWS AKIA/ASIA key IDs, Google AIza keys, and Supabase service_role and sb_secret_ keys. It also checks /.env, /.git/HEAD, HTTPS and four security headers.

It doesn't check your OpenAI usage, whether your server endpoint has auth or rate limits, or code that loads only after login. Those are the [manual] steps above.

FAQ

Can I hide my OpenAI key in the frontend with obfuscation or an env variable?
No. Anything the browser runs, the visitor can read. Variables like VITE_OPENAI_KEY or NEXT_PUBLIC_OPENAI_KEY are copied into your JavaScript at build time. The key must live on a server that the browser calls instead.
I revoked the key. Do I still need to change my code?
Yes. If the code still calls OpenAI from the browser, the next key you paste in will leak the same way. Move the call to a server function first, then create the new key and store it only there.
Will the free scan find my OpenAI key?
It looks for sk-proj- keys and OpenAI keys containing T3BlbkFJ in your page HTML and up to 25 scripts it links to. Keys in chunks that load only after login or a click may be missed, so also search in DevTools.

Scan your app free in 30 seconds

Paste your live URL for a plain-English report with fix prompts. If you keep shipping, weekly monitoring re-checks it: Solo $19/mo or Pro $49/mo.

Scan my app free