Lovable API key exposed: secrets visible in your client-side code
Symptoms
- Third-party keys (OpenAI, Stripe, SendGrid) are visible to anyone who opens DevTools or views the published app's bundled JS
- You find out the fun way: a key gets abused and the bill spikes, or a scanner flags the site
- Worst case: the Supabase service_role key, which bypasses RLS entirely, is sitting in frontend code
- Keys pasted into the Lovable chat persist in chat history that remixers of a public project can read
Why it happens
The AI takes the path of least resistance
Asked to 'integrate the OpenAI API', the AI calls the API directly from the browser with the key inline or in a VITE_-prefixed variable, because that is the shortest route to a working demo.
VITE_ variables are compiled into the public bundle
Anything with a VITE_ prefix ships to every visitor's browser. Lovable's own docs state that secrets in frontend code should be considered compromised.
Nothing errors, so it ships
An exposed key produces no failure signal. The app works, the demo impresses, and the leak sits there until someone abuses it.
How to fix it
- Search the code for leaked keysWith GitHub sync on, search the repo for key prefixes: sk-, sk_live_, service_role, and eyJ (JWTs). Anything found under src/ is compromised, full stop. Treat this as an active incident until rotation is done.
- Rotate every exposed key immediatelyGo to each provider's dashboard and rotate the key now. Removing a key from code does not un-leak it; it lives on in git history and in Lovable chat history. Honest note: any abuse that happened before rotation already happened, and rotation does not refund the bill.
- Move secrets to server-side storageStore secrets in Lovable Cloud > Secrets or as Supabase Edge Function secrets. Never give a secret a VITE_ prefix; the VITE_ prefix is precisely what makes a value client-visible.
- Proxy third-party APIs through an edge functionAsk Lovable: 'Create a Supabase Edge Function that proxies my [provider] API calls. Read the key with Deno.env.get() inside the function, and have the frontend call the function using only the anon key.' The browser then never sees the secret.
- Never paste secrets into the chat boxKeys pasted into the Lovable chat persist in chat history, and on a public or remixable project, anyone who remixes it can read that history. If you already pasted a key there, rotate that key too.
- Know your two Supabase keysThe anon key is safe to ship only if RLS is properly enabled on every table. The service_role key bypasses RLS entirely and must never appear anywhere client-side. If service_role is in your frontend, treat the entire database as exposed and rotate it first.
How to prevent it
- Store every key in Lovable Cloud > Secrets or edge function secrets from day one, never in code.
- Never use a VITE_ prefix for anything secret; that prefix means 'publish to every browser'.
- Never type a secret into the Lovable chat box, especially on public or remixable projects.
- Periodically search the synced GitHub repo for sk-, sk_live_, service_role, and eyJ.
- Keep the service_role key strictly server-side; the frontend gets the anon key only.
Tools that actually fix this
Recommended because they address the failure mode above, not because of the payout. Some are affiliate links; see how we choose.
A purpose-built secret scanner covering 450+ key types. Its ggshield pre-commit hook blocks a key from ever being pushed, and history scanning finds the ones you already leaked. Free for individuals.
Try GitGuardian →All-in-one appsec (code scanning, dependencies, secret detection, containers) with low-noise output, built for small teams that cannot run five separate tools. One dashboard covers several failure modes.
Try Aikido Security →Frequently asked questions
Where should I store API keys in a Lovable app?
Store API keys in Lovable Cloud > Secrets or as Supabase Edge Function secrets, and call third-party APIs through an edge function that reads the key with Deno.env.get(). The frontend should only ever hold the Supabase anon key. Lovable's own docs say any secret that reaches frontend code should be considered compromised.
If I delete an exposed API key from my code, is it safe?
No. Deleting a key from the code does not un-leak it: it persists in git history if the project is synced to GitHub, and in Lovable chat history if it was ever pasted there. The only real fix is rotating the key at the provider and moving the replacement into server-side secret storage.
Why did the AI put my API key in the frontend?
When you ask an AI builder like Lovable to integrate an API, its path of least resistance is calling that API directly from the browser with the key inline or in a VITE_-prefixed variable, and VITE_ variables are compiled into the public bundle. Nothing errors when this happens, so the app works and ships. That silence is the problem: key exposure produces no failure signal until the key is abused.
Is the Supabase service_role key ever safe in client code?
Never. The service_role key bypasses Row Level Security entirely, so anyone who extracts it from your bundle can read and write your whole database. It belongs only in server-side contexts such as edge functions. If it has ever shipped client-side, rotate it immediately and assume the data was accessible in the meantime.
Sources
- Lovable docs: Avoiding security pitfalls
- Lovable FAQ: Where to store API keys
- Vibe App Scanner: Lovable exposed API keys
- Vibe App Scanner guide: Fix Lovable API key exposure
- Momen: Security issues in Lovable (RLS, API key exposure, AI phishing)
- Lovable blog: A founder's guide to Lovable security
Related errors
The AI put your API key in the frontend: OpenAI, Stripe, or Supabase keys visible in the JS bundle
Accidentally committed an API key to GitHub
Replit API key exposed: secrets leaking from a public Repl
Lovable Supabase RLS not enabled: anyone can read your app's data (CVE-2025-48757)
Why is my OpenAI API bill so high? No rate limiting on your AI endpoints
The Vibe Oops briefing
One email when something ships to production that should not have. New incidents, new error guides, no filler.