# OpenAI or Gemini key in your frontend? Move it to an Edge Function

> Source: <https://dev.to/ahmed_hassanyaseen_1b7ab/openai-or-gemini-key-in-your-frontend-move-it-to-an-edge-function-3i73>
> Published: 2026-10-19 06:14:52+00:00

If your web app talks to OpenAI or Gemini from the browser, the API key is part of the JavaScript every visitor downloads. Anyone can copy it out and send requests that are billed to your account. Here's the order I fix it in: keep the key as a server secret, call the provider from an Edge Function that checks who is asking, then swap the leaked key for a new one.

If you got here by searching, it was probably one of these: `OpenAI API key exposed in frontend`, `dangerouslyAllowBrowser`, `VITE_OPENAI_API_KEY`, `Gemini API key exposed in client-side code`, "how to hide an API key in Lovable".

## 
  
  
  Why the key ends up public

- 
**The AI call lives in a component.** To get the feature working in preview, the builder calls OpenAI or Gemini directly from React, so the key ships with every page load.
- 
**It sits in a `VITE_` or `NEXT_PUBLIC_` variable.** Vite inlines every`VITE_` variable into the client bundle at build time; Next.js does the same with`NEXT_PUBLIC_` . Whatever file it came from, the value is public.
- 
**`dangerouslyAllowBrowser` is on.** The OpenAI JS library won't run in a browser without it, and its docs say the option exposes your secret credentials.
- 
**The key was pasted into a prompt or config file** , and the builder wrote it into frontend code.

## 
  
  
  Confirm it

1. Run the free Launch Check on the live site. It looks for OpenAI, Anthropic and Gemini keys in the JavaScript your site serves, and never displays the key.
2. DevTools → Network, then use the AI feature. A request going straight to `api.openai.com` or`generativelanguage.googleapis.com` means the key is in the browser.
3. Grep the code for `sk-proj-` ,`sk-ant-` ,`AIza` ,`dangerouslyAllowBrowser` , and any`VITE_` variable holding a key.
4. Check the provider's usage page for requests or spend you don't recognise.

## 
  
  
  Fix 1: move the call into an Edge Function

- Store the key as a secret, never under a `VITE_` name. In Lovable:**More > Cloud > Secrets** . In your own Supabase project:`supabase secrets set OPENAI_API_KEY=...` .
- Write an Edge Function that only accepts signed-in users, validates input, and reads the key with `Deno.env.get` . For Gemini, call the`generateContent` endpoint the same way, with the key in the`x-goog-api-key` header.
- In the page, call the function with `supabase.functions.invoke` and remove both the key and the provider SDK from the frontend.

## 
  
  
  Fix 2: replace the leaked key

Do this only after the function is live, or the new key leaks the same way.

- 
**OpenAI:** create a new key on the project's API keys page, save it as the server secret, then delete the leaked one.
- 
**Gemini:** create a new key in Google AI Studio (keys created there since 28 May 2026 are auth keys, limited to the Gemini API by default), save it as the secret, then delete the old key. Restrict any key you keep to the Gemini API.
- Deleting a key is final, so move every function and server to the new key first.

## 
  
  
  Fix 3: limit the damage meanwhile

- Set a budget alert in the provider's billing settings so unexpected spend reaches you the same day.
- Cap requests per user inside the function (for example a daily count stored in a table) so one account can't drain the budget.
- Note any usage you don't recognise, with dates, in case you need to raise it with the provider.

## 
  
  
  Prove it worked

- Launch Check again: no AI keys found.
- With DevTools open, the browser calls your function, never the provider.
- Calling the function while signed out is refused.
- The provider dashboard shows the leaked key deleted.

## 
  
  
  Keep it from coming back

- Provider keys live only in server secrets: never in `VITE_` /`NEXT_PUBLIC_` , never with`dangerouslyAllowBrowser` .
- When you ask the builder for an AI feature, ask for it "through an Edge Function, with the key stored in Secrets" from the first prompt.
- Turn on GitHub secret scanning, and run the Launch Check after every publish.

## 
  
  
  Quick answers

**Can an env variable hide the key?** Not in the frontend. Vite writes `VITE_` values into the JavaScript at build time. Only a server or Edge Function keeps it secret.

**Is a Gemini key safe if it's restricted to my domain?** Not for a key that can spend money. A website restriction checks the Referer header, which a script outside a browser can set to anything. Google's guidance is to call the Gemini API from your server.

**Does Lovable's built-in AI have this problem?** Not by design: it uses `LOVABLE_API_KEY`, a secret only Edge Functions can read, and Secrets offers a Rotate action for it. The risk is a key you add yourself and call from the page.

**Will the provider disable a leaked key for me?** Don't count on it. Until a provider acts, every request made with your key is billed to you.

Sources: OpenAI API reference (Authentication, JavaScript library browser use), Google AI for Developers (Using Gemini API keys), Vite (Env variables and modes), Lovable (Secrets), Supabase (Edge Function secrets, Securing Edge Functions). Links are on the original page.

I first wrote this up for our fix library at StacksMesh: [OpenAI or Gemini API key exposed in your app's code](https://stacksmesh.com/fix/ai-api-key-exposed-in-frontend?utm_source=devto&utm_medium=article&utm_campaign=BR-0002), with links to every official source. If you want to check your own live app for exposed keys, the free [Launch Check](https://stacksmesh.com/tools/launch-check?utm_source=devto&utm_medium=article&utm_campaign=BR-0002) does it in about a minute.

I wrote this with AI assistance and reviewed it myself.
