Skip to content
SealMyApp

Lovable API key exposed: what to do now

If a secret API key ended up in your Lovable app's code or chat, rotate it at the provider first, then store the new one in Lovable's Secrets and call the API from an edge function.

Updated 2026-10-16

Revoke the exposed key at the service that issued it (OpenAI, Stripe, Resend and so on) and create a new one, before you touch your code: deleting it from the code doesn’t un-leak it. Then save the new key in Lovable’s Secrets and call the API from an Edge Function, so the key never reaches the browser.

Lovable’s own security docs say API keys and other secrets can’t be stored safely in client-side code. Your Lovable frontend is React code that runs in every visitor’s browser, so anything in it, including a key in a component or a VITE_ variable, can be read in dev tools.

Step 1: Rotate the key now (5 minutes)

  1. Sign in to the provider’s dashboard and find its API keys page.
  2. Create a new key. Give it the narrowest permissions the provider allows (restricted keys, per-project keys, spending limits).
  3. Revoke or delete the exposed key. Some providers call this “rolling” the key.
  4. Check the provider’s usage or billing page for activity you don’t recognise since the key was exposed. If you see any, contact the provider.

Keep the new key somewhere safe for a minute; you’ll paste it into Lovable in step 2. Don’t paste it into your code.

Step 2: Store the new key in Lovable Secrets

  • Lovable Cloud: go to More → Cloud → Secrets and choose Add secret. Name it clearly, for example OPENAI_API_KEY. Lovable’s Secrets docs say these values are available to Edge Functions and are never included in your frontend code.
  • Your own Supabase project: store it as an Edge Function secret in that Supabase project instead.
  • If a secret with the old value already exists, save the new value over it. Lovable’s docs describe secrets as write-only, so you replace rather than edit them.

Step 3: Move the API call to the backend

The simplest route is to ask Lovable. Its docs say that if you describe the feature, it will store the key in Secrets, make the API call in server-side code and call that from the frontend. Paste this:

My <provider> API key was exposed in the frontend and I've rotated it.
1. Remove the key and any direct calls to <provider> from all frontend code and
   from any VITE_ variable.
2. Create an Edge Function that reads the key from Secrets (name: <SECRET_NAME>),
   checks the caller is a signed-in user, and makes the <provider> request.
3. Call that Edge Function from the frontend instead.
4. Show me every file you changed.

What the result should roughly look like, so you can check it. The function reads the secret on the server and refuses callers who aren’t signed in:

// supabase/functions/ask-ai/index.ts (runs on the server, not in the browser)
import { createClient } from "npm:@supabase/supabase-js@2";

const cors = {
  "Access-Control-Allow-Origin": "https://your-app.example",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};

Deno.serve(async (req) => {
  if (req.method === "OPTIONS") return new Response("ok", { headers: cors });

  // Only signed-in users may use your key
  const supabase = createClient(
    Deno.env.get("SUPABASE_URL")!,
    Deno.env.get("SUPABASE_ANON_KEY")!,
    { global: { headers: { Authorization: req.headers.get("Authorization") ?? "" } } },
  );
  const { data: { user } } = await supabase.auth.getUser();
  if (!user) return new Response("Unauthorised", { status: 401, headers: cors });

  const { prompt } = await req.json();
  const res = await fetch("https://api.provider.example/v1/...", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${Deno.env.get("OPENAI_API_KEY")}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ prompt }),
  });
  return new Response(await res.text(), { status: res.status, headers: cors });
});
// In the React frontend: no key anywhere
const { data, error } = await supabase.functions.invoke("ask-ai", {
  body: { prompt },
});

The user check matters: a function that forwards anything to a paid API without checking the caller just moves the leak, because anyone can call it and spend your credit. Add a per-user limit if the API costs money.

Step 4: Confirm it’s gone

  1. Publish, open your live app, and in dev tools search the loaded JavaScript for the start of the old and new keys (for example sk-, sk_live_, re_). Nothing should match. Our guide to finding leaked keys in your bundle walks through this.
  2. Run a Deep scan from Lovable’s Security view: Lovable’s docs say it checks code for secrets, which the Quick scan doesn’t.
  3. If your project syncs to GitHub, the old key is still in the commit history. It’s harmless once revoked, but don’t make the repo public without cleaning it.

FAQ

Lovable's scan flagged my Supabase anon key. Is that exposed?

The Supabase anon or publishable key is designed to be in your frontend; what matters is Row Level Security. See is the Supabase anon key safe to expose. A service_role or sb_secret_ key is different: follow what to do if you leaked it.

Can I put the key in a VITE_ variable instead?

No. Lovable's Secrets docs describe VITE_ values as build-time values exposed to the browser, and Vite's docs say not to put API keys in them. Secret keys go in Secrets and are read by backend code.

I deleted the key from my code. Am I safe now?

Not until you've revoked it at the provider. Deleting it from the current code doesn't remove it from earlier builds, Git history or anyone's copy.

What about LOVABLE_API_KEY?

That's the key Lovable uses for its built-in AI features. Lovable's docs say it has a Rotate action in Secrets (editor access or above) that issues a new key and updates your Edge Functions automatically.

Does Lovable warn me about leaked keys?

Lovable's docs say it detects keys pasted into the chat and steers them into Secrets, and that the Security view's Deep scan checks code for secrets. The Quick scan that runs on publish doesn't review application code, so run a Deep scan after changes.

Check it for free

Our free scan searches your live app’s JavaScript for secret keys (Stripe, OpenAI, Anthropic, AWS, Supabase service_role and others) and tests your database from the outside, after you verify you own the app. Not sure whether a key is secret? The key checker tells you from its prefix. Run a free scan, and see the Lovable pre-launch checklist for the rest.

Sealed — a short weekly security note for vibe coders

One common security mistake in AI-built apps, how to fix it in your builder, and one useful tool. No spam; unsubscribe any time.