Your OpenAI key leaked. Here's what to do
No OpenAI API key is safe to expose. Every key, whether it starts with sk-proj- or the older sk-, lets whoever holds it make requests billed to your account, so it belongs on a server. If one leaked, revoke it on the API keys page, create a new one, and check your usage.
Updated
Check your live site now
Free, no signup, read only. A grade and plain fixes in seconds.
What OpenAI keys look like
- sk-proj- followed by a long string: a project API key, the default today. It belongs to one project and can be given all, restricted or read-only permissions.
- sk- followed by a long string: the older user-level key format. Some accounts still have them.
- Other sk- variants exist for things like service accounts. Same rule applies.
There is no publishable OpenAI key. Unlike Stripe or Supabase, nothing in the OpenAI key family is designed to sit in a browser, and there's no setting that makes one safe there.
How OpenAI keys end up in front ends
- Calling api.openai.com straight from React. The official SDK refuses to run in a browser unless you pass dangerouslyAllowBrowser: true. If that flag is in your code, the key is in your bundle.
- A public env var name like VITE_OPENAI_API_KEY, NEXT_PUBLIC_OPENAI_API_KEY or EXPO_PUBLIC_OPENAI_API_KEY. The prefix exists to copy the value into client code.
- A mobile or desktop app with the key compiled in. App bundles can be unpacked.
- A committed .env file, or a key pasted into a prompt that the agent then wrote into source.
What someone can do with a leaked OpenAI key
- Requests billed to you until the key is revoked or your budget or credits run out.
- Your rate limits get used up, so your real app starts failing with rate limit errors.
- Depending on the key's permissions, access to what's stored in that project, such as uploaded files and vector stores.
- Your organisation's name on whatever the other person generates, which matters if it breaks usage policies.
OpenAI says it disables keys it detects on the public internet or inside published apps, but you can't count on it finding yours first, so act as if it hasn't.
Revoke and replace a leaked OpenAI key
- Open the API keys page in the OpenAI platform dashboard. Keys are listed per project, with a short visible fragment so you can match the leaked one.
- Revoke the leaked key. Requests using it fail from that moment.
- Create a new key in the right project. If your app only needs a few endpoints, give it restricted permissions.
- Store it in your host's environment variables as OPENAI_API_KEY, with no public prefix, and redeploy.
- Remove the old key from code, committed env files and git history.
- Check the usage page for the days the key was exposed. Unfamiliar models or usage at odd hours are the tell. Then set a project budget so a future leak has a ceiling.
# 1. Put the leaked value in a file, one per line, mapped to a placeholder
echo 'PASTE_THE_LEAKED_KEY_HERE==>REMOVED' > replacements.txt
# 2. Rewrite every commit (work on a fresh clone, keep a backup)
git filter-repo --replace-text replacements.txt
# 3. Force push the rewritten branches, then delete replacements.txt
git push --force --allHow to call OpenAI from a server route
Your front end sends the user's message to your own route. The route adds the key, calls OpenAI and returns only the answer.
import OpenAI from "openai";
const client = new OpenAI(); // reads OPENAI_API_KEY from the server env
export async function POST(req: Request) {
// Check the user is signed in and under their rate limit here.
const { message } = await req.json();
const response = await client.responses.create({
model: process.env.OPENAI_MODEL!, // fixed on the server, not sent by the client
input: String(message).slice(0, 4000),
max_output_tokens: 800,
});
return Response.json({ text: response.output_text });
}- Require sign-in, or at least a CAPTCHA, before the route does anything.
- Rate limit per user or per IP.
- Fix the model, system prompt and token caps on the server. Never forward a raw request body from the browser to OpenAI.
A route that forwards any request to OpenAI for anyone lets strangers spend your credits just as a leaked key would. For voice apps, OpenAI's Realtime API supports short-lived client secrets that your server mints for a browser session, so the real key still never leaves the server.
Our scan looks for sk- and sk-proj- keys in your page HTML and your site's own JavaScript bundles, which is where front-end leaks show up.
Questions
Is it safe to keep my OpenAI key in a .env file?
On the server, yes, as long as the variable has no public prefix and the file is in .gitignore. Use a .env.example with names only for sharing.
Can I restrict an OpenAI key to my website?
There's no setting that makes an OpenAI key safe to use from a browser. Restrict what it can do with project permissions and budgets, and keep it on a server.
How do I know if someone else used my key?
Look at the usage page for the affected dates and project. Spend or models you don't recognise mean someone else had it. Revoke first, investigate second.
Does GitHub catch OpenAI keys?
GitHub secret scanning recognises OpenAI API keys, and push protection can stop a commit that contains one. It won't see a key in your deployed JavaScript.