Skip to content
StackSiren

I leaked my OpenAI API key. What should I do now?

By StackSiren · Published

Revoke the key in the OpenAI dashboard right now, create a new one, and check your usage for activity you don't recognise. Then find out how it leaked, because if the cause was a front-end variable or a committed .env file, the new key will leak the same way.

Leaked model keys are attractive because they can be used to run requests billed to you. You can't un-publish a key, but you can make it worthless in under a minute. This guide covers the first hour, then the root-cause fix for apps built with Bolt, Lovable, Cursor, Replit and v0.

The first 10 minutes

1. Revoke the key

Sign in at platform.openai.com, go to API keys, find the key (the dashboard shows a short prefix and suffix) and delete or revoke it. Requests using that key start failing once it's revoked. If you're not sure which key leaked, revoke every key used by that project and create fresh ones.

If the key belongs to a project inside an organisation, revoke it in that project's key list. Project-scoped keys are a good reason to give each app its own project, so a leak only affects one app.

2. Create a replacement, but don't deploy it yet

Create the new key and keep it in a password manager for now. Putting it back in the same place as the old one recreates the leak. You'll add it to a server-side secret in the fix below.

3. Check usage and billing

Open the Usage page and look for spikes, models you don't use or traffic at hours your app is quiet. Check the billing page for unexpected charges. If you see abuse, contact OpenAI support through the help centre and explain that the key was compromised and has been revoked. We can't tell you what they'll decide about charges; that's between you and OpenAI.

4. Set limits

In your organisation's limits and billing settings, set a monthly budget and notification thresholds that match what your app really uses. Exact options vary by account type, so set whatever your dashboard offers. A budget won't stop a leak, but it caps the damage from the next one.

Find how it leaked

Work through these in order. Only check sites and repositories you own.

A. Is it in your front-end JavaScript?

This is the most common cause in AI-built apps. If the key was in a variable starting with VITE_ (Vite, used by many Bolt and Lovable projects) or NEXT_PUBLIC_ (Next.js, used by v0 and many Cursor projects), it was copied into the browser bundle at build time. Vite's docs say plainly that VITE_* variables "should not contain sensitive information such as API keys".

Check your live site:

SITE=https://yourapp.com
curl -s "$SITE/" | grep -oE '(/assets/[^"]+\.js|/_next/static/chunks/[^"]+\.js)' | sort -u | while read js; do
  curl -s "$SITE$js" | grep -qE 'sk-(proj-)?[A-Za-z0-9_-]{20,}' && echo "Possible OpenAI key in $js"
done

Or in the browser: DevTools → Sources → search all files (Ctrl/Cmd + Shift + F) for sk-. Background: are VITE_ variables secret? and is NEXT_PUBLIC_ safe?. Our exposed API key checker automates this.

B. Is your .env file publicly served?

Some static hosting setups serve every file in the build folder, including a stray .env:

curl -s -o /dev/null -w "%{http_code}\n" https://yourapp.com/.env
curl -s -o /dev/null -w "%{http_code}\n" https://yourapp.com/.git/config

A 200 with real content is a leak. Note that some single-page apps return 200 with the normal index.html for every path, so look at the body, not only the status code. See the exposed .env and .git guide, or run the env file checker.

C. Is it in a public source map?

Source maps (.js.map) contain your original source code. If the key was ever hard-coded, even in a file that's since been refactored into a server route, an old map might still have it:

curl -sI https://yourapp.com/assets/index-abc123.js.map | head -1

See the exposed source maps guide.

D. Is it in Git history?

If the repo is or ever was public, or shared with people who shouldn't have the key:

git log -p --all -S 'sk-' -- . ':(exclude)node_modules' | grep -nE 'sk-(proj-)?[A-Za-z0-9_-]{20,}' | head

Deleting the file in a new commit doesn't remove it from history. Rewriting history (GitHub documents git filter-repo for this) is optional once the key is revoked. Revocation is what actually protects you; rewriting history mostly stops future confusion. GitHub's secret scanning can also alert you to supported secret types pushed to repositories, depending on your plan and settings.

E. Did you paste it somewhere?

Screenshots, screen recordings, support tickets, Discord or forum posts, shared Replit or Bolt projects, and AI chat logs. Search for the first few characters of the key.

Fix the cause: move the key server-side

The browser should never hold a long-lived OpenAI key. It should call your endpoint, which calls OpenAI.

Next.js (v0, Cursor): app/api/chat/route.ts

import "server-only";

export async function POST(req: Request) {
  const { message } = await req.json();
  const r = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ model: "gpt-4o-mini", messages: [{ role: "user", content: String(message).slice(0, 4000) }] }),
  });
  return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
}

Supabase Edge Function (Lovable, Bolt + Supabase): store the key with supabase secrets set OPENAI_API_KEY=... and read it with Deno.env.get("OPENAI_API_KEY") inside the function. A full example is in are VITE_ variables secret?.

Replit: store the key in Replit's Secrets tool and read it from process.env.OPENAI_API_KEY in server code only. Don't print it to logs. Replit's docs describe how Secrets behave when a Repl is shared or remixed; check them before sharing a project.

Then:

  1. Add OPENAI_API_KEY (no VITE_ or NEXT_PUBLIC_ prefix) to your host's server environment.
  2. Remove every front-end reference to the old variable, and remove dangerouslyAllowBrowser: true.
  3. Require a signed-in user on the endpoint and add rate limiting. An open proxy to OpenAI is almost as bad as a leaked key.
  4. Rebuild, redeploy and re-run check A.

FAQ

Is deleting the commit or the variable enough? No. Copies may already exist in caches, forks or scrapers' databases. Revocation is the only fix that works everywhere at once.

Will OpenAI refund charges from a leaked key? We don't know and can't promise anything. Contact OpenAI support with the details; their decision depends on their policies.

How fast are leaked keys found? We don't have reliable data to quote. Assume it can be quick and revoke immediately rather than waiting to see.

Can I restrict an OpenAI key to my domain like a Google Maps key? Not in the same way, as far as we can confirm from OpenAI's docs. That's why the key must stay on a server. Project-scoped keys and budgets reduce the blast radius.

Does the same apply to Anthropic, Gemini, Stripe or Resend keys? Yes. Revoke, check usage, find the cause, move the key server-side. Provider steps are in the exposed API keys guide.

How do I get warned next time? Run an external scan after every deploy. StackSiren's first scan is free and passive, and paid plans (Solo $29/mo, Builder $79/mo) add scheduled rescans of sites you own.

Check your app now

Free passive scan, results in under a minute.

Run a free scan

Fix guides for this topic

Platform checklists: Bolt · Lovable · Cursor · Replit · v0

Related posts

Sources