# Are Your API Keys Exposed? How to Check Your Lovable, Bolt or Cursor App's JavaScript

> Source: <https://dev.to/replaywatch/are-your-api-keys-exposed-how-to-check-your-lovable-bolt-or-cursor-apps-javascript-jf3>
> Published: 2026-11-03 20:09:06+00:00

You asked your AI builder to "connect Stripe" or "add an AI chat", it worked first time, and you shipped. That's what tools like Lovable, Bolt, Replit and Cursor are good at. But there's one question to ask before you share the link with anyone: **did a secret key end up in the code that runs in your visitor's browser?**

If it did, anyone can copy it. You don't need to be a developer to check, and the fix usually takes one prompt. This guide shows you how.

Most apps built with these tools are a React (often Vite) frontend that talks to services like Supabase, Firebase, Stripe or OpenAI. When you publish, all of your frontend code is bundled into JavaScript files and sent to every visitor's browser.

That means:

`VITE_` is deliberately `NEXT_PUBLIC_`.
So the question isn't "is my key hidden?" It's "is this the kind of key that's *allowed* to be public?"

**Usually fine in the frontend (designed to be public):**

`apiKey`, `authDomain`, `projectId`…). It identifies your project. Protection comes from Firebase Security Rules.`pk_live_…` / `pk_test_…`).
**Never in the frontend:**

`service_role` key.`sk_live_…`) or restricted keys (` rk_live_…`). These can create charges, issue refunds and read customer data.`sk-…`, `sk-proj-…`, `sk-ant-…`). Anyone can run up your bill.` postgres://user:password@…`), webhook signing secrets, JWT secrets, and GitHub tokens.
It's not that the AI is careless. It's that you asked for the shortest path to "it works". A few common ways it happens:

Do this on your **published URL**, not the editor preview, because what matters is what real visitors get.

`sk_live` and `rk_live`
`sk-` (catches many AI provider keys, so expect some noise)`secret`` postgres://``Bearer`
Also search for the start of any key you remember pasting into your builder.

Supabase's legacy keys are JWTs: long strings starting with `eyJ`. Paste one into a JWT decoder you trust that runs locally, or just look at the middle section. If the payload contains `"role":"anon"`, it's the public key. If it says `"role":"service_role"`, **you have a problem.**

`api.openai.com`, `api.stripe.com` and similar. If your `Authorization` header, the key is exposed even if you couldn't find it by searching.
**1. Rotate the key first, then fix the code.** Removing a key from the code doesn't make it safe. It's already been public, possibly for weeks, and may be cached or copied. Go to the provider's dashboard and roll or revoke it:

**2. Move the call to the server side.** Prompt your builder with something like:

"Move all calls that use [OpenAI / Stripe secret / service_role] into a Supabase Edge Function (or server route). Store the key as a server-side secret, not a VITE_ variable. The frontend should call the function, never the provider directly. Remove the key from every frontend file and from .env files that are bundled."

In Lovable and Bolt projects backed by Supabase, that usually means an **Edge Function plus Supabase secrets**. In Replit, use **Secrets** and a backend route. In a Cursor/Next.js project, use a **server route or server action** with a variable that does *not* start with `NEXT_PUBLIC_`.

**3. If it was the `service_role` key, fix the reason you needed it.** It was almost always added to get around an RLS error. Put back the anon key and write proper policies (for example, users can select rows where `auth.uid() = user_id`).

**4. Check your provider dashboards for abuse.** Look for unusual usage, unexpected charges, or new records you didn't create.

**5. Check git history.** If the project syncs to GitHub, the key is in your commit history too. Rotating it (step 1) is what actually protects you. Rewriting history is optional clean-up.

A public key is only safe if the thing behind it is locked down:

`allow read, write: if true` = public database.
Secrets tend to come back. A later AI edit can reintroduce a direct API call, especially when you ask it to "just make it work". Add these searches to your release checklist and run them after every significant change, alongside checking that signup, login and your core flow still work.

If you'd like a quick outside check, **ReplayWatch** (disclosure: I built it) runs a free scan of your live URL at [https://replaywatch.com](https://replaywatch.com). It looks for secrets exposed in your HTML/JS and for publicly readable Supabase/Firebase data, plus security headers, broken links and assets, SEO tags, page load, and whether your signup page is reachable. It only looks at what's publicly accessible and doesn't log in or run your signup flow, so keep testing those by hand.
