Skip to content

Guides · VibeVigil

How to Check if Your Vibe-Coded App Is Leaking API Keys

A plain-English checklist to find secret API keys hiding in your Lovable, Bolt, v0 or Cursor app: in the browser code, in .env files, in source maps and in GitHub history. Plus what to do if you find one, and a copy-paste fix prompt for your coding agent.

AI coding tools are great at making things work. Sometimes "working" means a secret key got pasted into code that runs in the browser. If that happens, anyone who opens your site can copy the key and use it. They can run up your AI bill, read your database, or send email as you.

This guide shows you how to check your own app. You don't need to be a developer.

The short answer

  1. Open your live app, press F12 (or right-click and pick Inspect), and go to the Sources or Network tab.
  2. Search all files for these words: sk_, sk-, sb_secret_, service_role, OPENAI, ANTHROPIC, STRIPE, RESEND, secret.
  3. Anything that looks like a real secret key is a leak. Rotate that key today (see "If you find a leaked key" below).

Some keys are meant to be public. The next section shows you which ones.

Public keys vs. secret keys

Not every key in your code is a problem. Here is a quick guide:

KeySafe in the browser?Why
Supabase publishable key (sb_publishable_...) or legacy anon keyYesIt only reaches what your Row Level Security (RLS) rules allow.
Supabase secret key (sb_secret_...) or legacy service_role keyNoIt skips every RLS rule. It is the master key to your data.
Stripe publishable key (pk_live_...)YesIt can't move money on its own.
Stripe secret key (sk_live_...) or restricted key (rk_live_...)NoIt can read customers and make charges and refunds.
OpenAI, Anthropic, Gemini and other AI keysNoAnyone can use them and you pay the bill.
Email keys (Resend, SendGrid, Postmark)NoSomeone could send spam or phishing from your domain.

Supabase's own docs put it simply: the publishable key is "safe to expose online," but secret keys "provide full access to your project's data, bypassing Row Level Security." (Supabase: API keys)

Heads up: Supabase says it is deprecating the old anon and service_role keys by the end of 2026, and moving to publishable (sb_publishable_) and secret (sb_secret_) keys. If an AI tool tells you to paste a long key that starts with eyJ, it is using the old style. (Supabase: API keys)

Step 1: Search the code your visitors download

Everything in the browser is public. Check it like a stranger would.

  1. Open your live site in Chrome or Edge.
  2. Press F12 and open the Sources tab.
  3. Press Ctrl+Shift+F (Windows) or Cmd+Option+F (Mac) to search across all files.
  4. Search for each of these, one at a time:
sk_live    sk_test    rk_live    sk-    sb_secret_    service_role
OPENAI     ANTHROPIC  GEMINI     RESEND    SENDGRID    secret    password
  1. Also open the Network tab, reload the page, and click around your app. Look at the request headers. A secret key should never be sent from the browser.

If you find service_role inside a long eyJ... key, you can paste that key into a JWT decoder you trust offline. The role field will say anon (fine) or service_role (leak).

Step 2: Check your environment variable names

Most vibe-coded apps use Vite (Lovable, Bolt) or Next.js (v0, many Cursor apps). Both tools copy certain variables into the browser code on purpose:

So open your .env file (or your host's environment settings) and look for names like these:

VITE_OPENAI_API_KEY          <- leak
NEXT_PUBLIC_STRIPE_SECRET    <- leak
VITE_SUPABASE_SERVICE_ROLE   <- leak
VITE_SUPABASE_PUBLISHABLE_KEY  <- fine

Rule of thumb: if a variable has a public prefix, it must hold only a public key.

Step 3: Make sure your .env file isn't public

Some hosts serve every file in your project folder. Try opening these in your browser, using your own domain:

https://your-app.com/.env
https://your-app.com/.env.local
https://your-app.com/.git/config

You should get a "not found" page or your normal app page. If you see text with keys or settings, the file is public. Rotate every key in it and block the file on your host.

Step 4: Check source maps

Source maps are files that turn your shrunk-down code back into readable code. They make debugging easy, but they also show your original code to anyone.

In the Sources tab, look for your original file names (like src/lib/openai.ts). If you can read them, your source maps are public. That's not always a disaster, but any secret in your source code is now easy to find. Turn source maps off for production unless you need them.

Step 5: Check your GitHub history

If your app syncs to GitHub (Lovable, Bolt and Cursor all can), a key might live in an old commit, even if the file is gone now. Deleting a file does not remove it from history. (GitHub: Removing sensitive data from a repository)

If you are comfortable with a terminal, run this in your project folder:

git log -p -S "sk_live" --all
git log -p -S "service_role" --all
git log -p -S "OPENAI_API_KEY" --all

Any result means the key was committed at some point. Treat it as leaked, especially if the repo is public or was ever public. Also make sure .env is listed in your .gitignore file.

GitHub has built-in secret scanning and push protection that can catch many keys before they land. Turn it on in your repo's Settings → Code security. (GitHub: About secret scanning)

If you find a leaked key

Do these in order:

  1. Make a new key in that service's dashboard (Supabase, Stripe, OpenAI and so on).
  2. Move the new key to the server. Put it in a backend function, like a Supabase Edge Function, a Next.js server route, or your host's server environment. Never give it a VITE_ or NEXT_PUBLIC_ name.
  3. Update your app so the browser calls your server function, and your server function calls the paid service.
  4. Turn off the old key once everything works with the new one. For Supabase, you make a new secret key, swap it in, then delete or deactivate the old one. (Supabase: Rotate a leaked key)
  5. Check the bill and the logs for the service. Look for use you don't recognize. For AI keys, set a monthly spending limit if the provider offers one.

Removing the key from your code is not enough on its own. Anyone who copied it can still use it until you turn it off.

Paste this prompt into your coding agent

Works in Lovable, Bolt, v0, Cursor or Claude Code:

Audit my app for leaked API keys and fix it. Don't delete any data.

1. Search the whole project, including .env files and config, for secret keys:
   Stripe sk_/rk_, Supabase service_role or sb_secret_, OpenAI, Anthropic, Gemini,
   Resend, SendGrid, and anything named SECRET, TOKEN or PASSWORD.
2. List every environment variable that starts with VITE_ or NEXT_PUBLIC_ and tell me
   which ones hold a secret. Only public keys may use those prefixes.
3. Move every secret key to server-side code (a Supabase Edge Function or a server route).
   The browser must call my server, and only the server may call the paid API.
4. Make sure the frontend uses only the Supabase publishable (or anon) key, never the
   secret or service_role key.
5. Make sure .env and .env.* are in .gitignore and are not served by my host.
6. Turn off public source maps for the production build.
7. Give me a list of every key I need to rotate, with the dashboard where I rotate it.
8. Explain each change in plain English and tell me how to test that the app still works.

After the fix, repeat Step 1 on your live site. Your searches should come back empty, except for keys that are meant to be public.

FAQ

Is it OK that my Supabase key is visible in my code? If it's the publishable (sb_publishable_) or anon key, yes. That's by design. Your RLS rules protect your data. See our guide on how to check if Supabase RLS is enabled. The secret or service_role key must never be in the browser.

My app is small. Would anyone really find my key? Bots scan public sites and public GitHub repos for keys all the time. It's not about how famous your app is. A key in public code should be treated as found.

I asked my AI to "hide" the key, and it put it in a .env file. Am I safe now? Not always. If the variable name starts with VITE_ or NEXT_PUBLIC_, the key still ends up in the browser. The key needs to live in server-side code.

I deleted the key from my code. Do I still need to rotate it? Yes. Old versions of your site, GitHub history, and anyone who copied it may still have it. Make a new key and turn off the old one.

Sources

  • Supabase docs, API keys: https://supabase.com/docs/guides/api/api-keys
  • Vite docs, Env Variables and Modes: https://vite.dev/guide/env-and-mode
  • Next.js docs, Environment Variables: https://nextjs.org/docs/app/guides/environment-variables
  • GitHub docs, Removing sensitive data from a repository: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository
  • GitHub docs, About secret scanning: https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning

Scan your app free. It checks the public page of a site you own. No account.

How to Check if Your Vibe-Coded App Is Leaking API Keys · VibeVigil