You put a secret API key in your frontend (and how to undo it)
One error, one plain-English fix you can paste into your AI tool.
Last checked against official docs: 19 Oct 2026. Educational information, not a security audit.
Symptom
- Your code (or your AI tool) put a key like
sb_secret_..., a longeyJ...service_role key,sk_live_...(Stripe), or an OpenAI/other AI-provider key in a file the browser loads, or in a variable starting withVITE_/NEXT_PUBLIC_. - You got an email from GitHub/your provider saying a secret was detected, or surprise usage on your AI bill.
Why it happens (plain words)
Everything your browser app ships is public. Anyone can open DevTools and read it. Build tools copy any variable with a "public" prefix into that bundle: Vite says VITE_* variables "are bundled into your source code at build time" and "should not contain sensitive information such as API keys." Next.js does the same with NEXT_PUBLIC_.
There are two kinds of keys:
- Publishable / public keys (Supabase
sb_publishable_...or legacyanon, Stripepk_...): OK in the browser. They only work within the limits you set (for Supabase, your RLS rules). - Secret keys (Supabase
sb_secret_...or legacyservice_role, Stripesk_..., AI-provider keys): never in the browser. The Supabase secret key bypasses every RLS rule.
How to confirm
- Open your live site → DevTools → Sources (or view source), and use search (Ctrl/Cmd+Shift+F) for:
sb_secret,service_role,sk_live,sk_test,sk-,secret. - In your project, search
.envfiles and code for variables starting withVITE_/NEXT_PUBLIC_that hold secrets. - Check whether
.envis committed to Git (it shouldn't be).
Step-by-step fix
- Move the secret to the server side. Put the call that needs the secret in a backend function (Supabase Edge Function, Vercel/Netlify serverless function, your host's server route). The browser calls your function; only the function holds the key.
- Store the secret as a server-side env variable on your host, without a public prefix (e.g.
SUPABASE_SECRET_KEY,STRIPE_SECRET_KEY,OPENAI_API_KEY). In Lovable, use its secrets feature for backend functions. - In browser code, use only publishable keys (Supabase publishable key + correct RLS; see the RLS guide).
- Protect the backend function: check the user is logged in and allowed before doing anything. Otherwise you've just moved the open door.
- Rotate (replace) every key that was exposed. Removing it from code is not enough: it's already been public, and Git history keeps it.
- Supabase: create a new secret key in Settings → API Keys, swap it everywhere, confirm, then delete/deactivate the old one (official order from Supabase docs).
- Stripe / AI providers: roll or revoke the key in their dashboard and create a new one.
- Add
.env*to.gitignore.
Copy-paste prompt for your AI tool
Security fix, do not skip steps:
1. Search the whole project for secret keys or secret env vars used in browser/client code: anything like sb_secret, service_role, sk_live, sk_test, OpenAI/Anthropic/other API keys, or secrets in VITE_ / NEXT_PUBLIC_ variables. List each file and line.
2. For each one, move the logic that needs the secret into a server-side function (e.g. a Supabase Edge Function or serverless route) that reads the key from a server-only environment variable with no public prefix.
3. That function must verify the caller is signed in and authorized before acting, and must never return the key.
4. Update the frontend to call the new function and use only the publishable/anon key.
5. Make sure .env files are in .gitignore.
6. Give me a checklist of which keys I must rotate in which dashboards. Do not print any key values.
How to verify it's fixed
- Rebuild and redeploy, then repeat the DevTools search on the live site: no secret strings.
- The old keys no longer work (they've been revoked/rotated).
- The feature still works when logged in, and the backend function refuses requests when logged out.
Safety notes
- Never paste real secret keys into chats, AI prompts, tweets, screenshots or support tickets.
- Watch your provider billing/usage pages for a few days after a leak.
- If a Supabase secret key leaked, treat all project data as potentially accessed (it bypasses RLS).
Sources
- Supabase: API keys (key types, handling secrets, rotation), https://supabase.com/docs/guides/api/api-keys
- Vite: Env variables, "Protecting secrets", https://vite.dev/guide/env-and-mode
- Lovable: Security overview (API key protection), https://docs.lovable.dev/features/security
- Stripe: keys best practices, https://docs.stripe.com/keys-best-practices
