pingbuddy

blog

Exposed API key in your Lovable app — what to do now

A secret key (OpenAI, Stripe, Supabase service_role) ended up in your app's public code. Which keys are actually dangerous, how to revoke each one, and how to move the call to the server.

6 min readLovableSecurityFixes

Someone told you — or a scanner found — that your Lovable app has an API key in its public code. Maybe it's an OpenAI key, a Stripe secret key, or a Supabase service_role key.

First, don't panic, and don't just delete the line and republish. Deleting the key from the code doesn't help if someone already copied it. This guide walks you through what to do, in the right order.

Step 0: Is it actually a secret?

Not every key in your frontend is a problem. Some keys are designed to be public.

Fine to have in browser code:

  • Supabase anon key or publishable key (sb_publishable_... or the older long eyJ... anon JWT). Supabase's documentation says these are meant for public, client-side code, and that your data stays safe as long as Row Level Security (RLS) is on for your tables. More on RLS below.
  • Stripe publishable key (pk_live_... or pk_test_...). Made for the browser.
  • Analytics or map keys that the provider documents as public and lets you restrict by domain.

Never in browser code:

  • Supabase service_role key or secret key (sb_secret_...). Supabase's docs say these give full access to your project's data and bypass Row Level Security. Anyone with it can read, change or delete everything.
  • Stripe secret key (sk_live_...) or restricted key (rk_live_...). Can create charges, refunds and read customer data.
  • OpenAI keys (sk-..., sk-proj-...) and Anthropic keys (sk-ant-...). Anyone can use them on your bill.
  • Keys for email providers (Resend, SendGrid), AWS, GitHub tokens, and anything labeled "secret".

If yours is in the second list, keep going. Speed matters: leaked keys are found and abused by automated scanners, sometimes within minutes of being published.

Step 1: Revoke the key — before anything else

Removing the key from your code doesn't stop someone who already copied it. You have to make the key itself stop working. Do this first, even if it briefly breaks a feature.

OpenAI

OpenAI's help center says that if you believe a key has leaked, you should rotate it immediately from the API keys page. Their safe order: create a new key, put the new key where your server code needs it, then delete the old one. Since the old key is already public, delete it right away and accept a minute of downtime. Also set a monthly spending limit in your OpenAI billing settings while you're there.

Anthropic

In the Anthropic Console, open API keys, create a new key if you need one, and disable or delete the leaked one.

Stripe

Stripe's key best-practices guide says to rotate a compromised secret key in the Dashboard and replace the old key in your integration. In the Stripe Dashboard, go to Developers → API keys and use the option to roll the secret key. If Stripe offers a delay before the old key expires, choose to expire it immediately — it's already public. Then check your Stripe Dashboard for any activity you don't recognize, such as unexpected refunds, payouts or new API requests.

Supabase (service_role or secret key)

Supabase's API keys docs describe the process for a leaked secret key: in the project's API Keys settings, create a new secret key, switch everything that used the old one to the new one, then delete the compromised key. If your project still uses the older JWT-based service_role key, the docs recommend replacing it with the newer secret keys using the same process.

Because a service_role key bypasses Row Level Security, check your data afterwards: look for rows, users or files you don't recognize.

If a provider's dashboard is slow or throws errors while you do this, check whether the provider itself is having trouble: our live status pages for OpenAI, Anthropic, Stripe and Supabase show it at a glance.

Anything else

Every provider has a "revoke", "delete" or "roll" button on its API keys page. Use it. Then create a new key for your server-side code.

Step 2: Find out where the key is in your app

In Lovable, ask:

Search the whole project for API keys or secrets in frontend code: anything starting with sk_, sk-, rk_, sb_secret_, or a Supabase service_role key, and any variable with VITE_ in its name that holds a secret. List each file and line. Don't change anything yet.

Keys usually end up in the frontend in one of three ways:

  1. Pasted into the chat and written straight into a component.
  2. Stored as a VITE_ variable. Lovable's docs explain that VITE_ variables in .env are built into the published app — they're for public values. A secret with a VITE_ prefix is readable by every visitor.
  3. A direct call to an API from the browser, like calling OpenAI from a React component, which needs the key right there in the code.

Step 3: Move the secret to the server

The fix isn't just "hide the key". It's moving the call that needs the key to code that runs on a server, where visitors can't see it.

In a Lovable app with Supabase, that place is a Supabase Edge Function. Lovable's docs describe a Secrets area for exactly this: secrets stored there are available to Edge Functions, not to browser code. Lovable also detects API keys pasted into the chat and guides you to store them as secrets instead.

Ask Lovable:

Move the call to <OpenAI / Stripe / service> into a Supabase Edge Function.
Store the new key as a secret (not a VITE_ variable).
The frontend should call the Edge Function, never the service directly.
The Edge Function must check that the user is signed in before doing anything.
Remove the old key from the code and from .env.

The line about checking the user matters. An Edge Function that anyone on the internet can call with no checks is just a slightly slower way to leak your key's power.

Then add the new key (from Step 1) in Lovable's Secrets — not the old one, which is dead.

Step 4: If it was a Supabase key, check Row Level Security

Whether your leak was the service_role key or not, this is a good moment to check RLS, because it's what keeps your public anon key safe.

Supabase's "Securing your data" guide says you can safely use the anon key in the browser as long as RLS is enabled on your tables. Without RLS, anyone with your public key can read or change data in those tables directly.

  • In the Supabase dashboard, check each table that holds user data: RLS should be enabled, with policies that only allow what your app needs (for example, "users can read their own rows").
  • Lovable's security scan, which runs when you open the publish dialog, includes checks on RLS policies. Run it and fix what it flags.

Step 5: Clean up the history

If your project is connected to GitHub, the key is still in the commit history even after you remove it from the code. Since you've already revoked the key, the old one is useless — which is exactly why Step 1 comes first. You don't strictly need to rewrite history, but if the repository is public, consider making it private.

Step 6: Check for damage

  • OpenAI / Anthropic: look at usage for the last days. Unexpected spikes mean someone used the key.
  • Stripe: look at recent events and payments for anything you didn't do.
  • Supabase: look at the auth users list and your main tables for unexpected changes.
  • Email providers: check sending logs for messages you didn't send.

If you see abuse, contact the provider's support. Some will help with charges from a leaked key, but don't count on it.

How to make sure it doesn't happen again

  • Never paste secret keys into the AI chat unless you're adding them to Secrets.
  • Remember the rule: VITE_ means public. Only public keys get a VITE_ prefix.
  • Run Lovable's security scan before publishing, and don't publish past critical findings.
  • Scan your live app regularly. Code changes with every prompt, and a key can come back in a later edit.

Pingbuddy does the last part for you. Once a day, and right after every publish, it looks through your app's HTML and its own JavaScript files for known secret key formats — Stripe, OpenAI, Anthropic, Supabase secret and service_role keys, AWS, GitHub, SendGrid, Resend. If it finds one, you get an alert with the key type, its first 6 characters (never the full key — we don't store it), how to revoke that specific key, and a prompt to move it out of your code. It's on the Free plan.

Want to check right now? Run a free checkup of your app. It scans the page and its scripts for leaked keys and shows what it finds, with the same instructions. It takes about 10 seconds.

Summary

  1. Check whether the key is a public one (anon, publishable) or a secret.
  2. If secret: revoke it first, then create a new one.
  3. Find every place the key is used in the frontend.
  4. Move the call to a Supabase Edge Function, with the new key stored as a secret.
  5. Make sure RLS is on for your Supabase tables.
  6. Check for damage.
  7. Scan your live app regularly so it doesn't come back.

Monitor your app free with Pingbuddy

Paste your site's link. We check it every 5 minutes, open it in a real browser to catch blank screens, and message you on Telegram or email the moment it breaks — with a screenshot and a fix prompt. Free for up to 20 sites.

Or run a free checkup of your app first.