How to Fix Lovable API Key Exposure (2026)

A Lovable app with an exposed OpenAI key can drain your credits in hours. In our scan of Lovable-generated deployments, exposed API keys and Supabase service_role misuse were the two most common critical findings. The root cause is almost always one of two patterns: a variable named VITE_OPENAI_API_KEY that Vite bakes into the client JavaScript bundle at build time, or Lovable's AI generating a Supabase client in a React component using the service_role key.

This guide walks you through finding the leak, rotating the key, and fixing the root cause so it does not happen again.

TL;DR

Lovable API key exposure has two main causes: (1) secrets with a VITE_ prefix baked into the client bundle by Vite, and (2) Lovable generating code that uses your Supabase service_role key in React components. Rotate the exposed key immediately, then either remove the VITE_ prefix and move the call to a Supabase Edge Function, or switch from service_role to the anon key with RLS policies. Do not delay rotation waiting to confirm abuse.

Step 1: Find the Exposed Key

Before rotating, confirm exactly what is exposed and how.

1

Check your JS bundle in the browser. Open your deployed Lovable app, press F12, go to Sources, and search for your key value or its known prefix: sk_proj_ (OpenAI), sk_live_ (Stripe), AKIA (AWS), or eyJhbGci (Supabase JWTs). If it appears inside a .js file served from your domain, it is in your client bundle and visible to any visitor.

2

Search your project for VITE_-prefixed variables.

If you have downloaded your Lovable project or connected it to a GitHub repo:

# In your Lovable project directory
grep -r "VITE_" . --include="*.env*" --include="*.ts" --include="*.tsx" --include="*.js"

Any variable named VITE_OPENAI_KEY, VITE_ANTHROPIC_API_KEY, or VITE_STRIPE_SECRET is almost certainly in your bundle. Lovable uses Vite under the hood, and the VITE_ prefix is how values get shared with React components. It also exposes them to every visitor.

3

Check for hardcoded Supabase service_role usage. Lovable's AI sometimes generates a Supabase client in a React component using the service_role key:

# In your Lovable project files
grep -r "service_role" . --include="*.ts" --include="*.tsx" --include="*.js"
grep -r "SUPABASE_SERVICE_ROLE" . --include="*.ts" --include="*.tsx" --include="*.env*"

The anon key (VITE_SUPABASE_ANON_KEY) is designed to be public. The service_role key is not.

4

Check git history for committed secrets.

If your Lovable project is connected to a GitHub repo:

git log --all --full-history -- .env
git log --all --full-history -- .env.local
git log --all --full-history -- .env.production

If any of these return commits, secrets were stored in your repo history. Anyone who cloned or forked the repo has a copy.

CheckYourVibe scans your deployed Lovable app and flags API keys found in the JavaScript bundle, including OpenAI, Anthropic, Stripe, Supabase service_role, and AWS credentials. It reads what an attacker reads from your public bundle.

Step 2: Rotate the Exposed Key Immediately

Do not try to fix the code first. The key is already out. Rotate it now.

1

Generate a new key in the affected service's dashboard (OpenAI, Stripe, Supabase, etc.). Most services let you have multiple active keys so your production app stays live during the transition.

2

Update your environment variables. Depending on how your Lovable app is deployed:

  • Lovable-hosted apps: Go to your Supabase project, open Project Settings > Edge Functions > Secrets, and update the secret value there.
  • Deployed to Netlify: In Netlify's dashboard, go to Site Settings > Environment Variables, find the variable, and update its value. Netlify triggers a new deploy automatically.
  • Deployed to Vercel: In the Vercel dashboard, go to Settings > Environment Variables and update the value. Redeploy manually or push a commit to trigger a new deploy.
3

Redeploy your app. The new key only takes effect in your deployed app after a fresh build. Netlify and Vercel rebuild automatically on env var changes. For Lovable-hosted apps, a new deploy picks up updated Supabase Secrets.

4

Revoke the old key in the service dashboard. Delete or disable the compromised key. A rotated but not revoked key is still usable by anyone who copied it.

5

Check usage logs. Review the affected service's usage dashboard for spikes during the exposure window. OpenAI shows per-key usage in the API dashboard. If you see unexpected requests, note which endpoints were called and when.

Do not delay revocation waiting to confirm abuse. API abuse can cost thousands of dollars within hours of exposure. Rotate first, investigate second.

Step 3: Remove from Git History (if committed)

If your audit found secrets in git history, they are visible in every past commit, even if you deleted the file later.

# Install git-filter-repo
pip install git-filter-repo

# Remove .env from all history
git filter-repo --path .env --invert-paths
git filter-repo --path .env.local --invert-paths

After scrubbing, force-push to all remotes and notify any collaborators to re-clone. If the repo was public at any point, treat the key as compromised regardless of whether you can confirm it was accessed.

Add .env to your .gitignore if it is not already there:

echo ".env" >> .gitignore
echo ".env.local" >> .gitignore
echo ".env.*.local" >> .gitignore
git add .gitignore && git commit -m "chore: gitignore .env files"

Step 4: Fix the Root Cause for VITE_ Secrets

Rotating fixes the immediate risk. Fixing the root cause stops it from recurring.

Why VITE_ Variables Are Dangerous

Vite embeds every variable prefixed with VITE_ into the client bundle at build time. The prefix exists specifically to share values with browser code. Using it with a secret is the wrong tool for the job.

Wrong (key ends up in every visitor's browser):

// In a Lovable-generated React component - leaks key to the browser
const response = await fetch("https://api.openai.com/v1/chat/completions", {
  headers: {
    Authorization: `Bearer ${import.meta.env.VITE_OPENAI_API_KEY}`, // visible to all
  },
});

Right (key stays server-side via a Supabase Edge Function):

// supabase/functions/chat/index.ts - server-side only
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";

serve(async (req) => {
  const { message } = await req.json();
  const response = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${Deno.env.get("OPENAI_API_KEY")}`, // never reaches browser
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o",
      messages: [{ role: "user", content: message }],
    }),
  });
  const data = await response.json();
  return new Response(JSON.stringify({ reply: data.choices[0].message.content }), {
    headers: { "Content-Type": "application/json" },
  });
});

Your React component calls your Supabase Edge Function URL. The Edge Function calls OpenAI. The key never reaches the browser.

Variable Storage in Supabase

Store secrets in Supabase under Project Settings > Edge Functions > Secrets. Reference them in Edge Functions only:

WrongRight
VITE_OPENAI_API_KEY in ReactOPENAI_API_KEY in Supabase Secrets
VITE_ANTHROPIC_API_KEY in ReactANTHROPIC_API_KEY in Supabase Secrets
VITE_STRIPE_SECRET_KEY in ReactSTRIPE_SECRET_KEY in Supabase Secrets

Then access it in your Edge Function as Deno.env.get("OPENAI_API_KEY"). Supabase injects it only into server-side function code.

Step 5: Fix Supabase service_role Misuse

The Supabase service_role key bypasses every Row Level Security policy in your database. It is designed for backend admin operations only.

Lovable's AI occasionally generates a Supabase client in a React component or hook using the service_role key. The result: any visitor who opens DevTools has full read and write access to your entire database.

Wrong (service_role in a React component):

// In a Lovable-generated React hook - full database access for any visitor
import { createClient } from "@supabase/supabase-js";

const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_SERVICE_ROLE_KEY // never do this
);

Right (anon key in React, service_role only in Edge Functions):

// React component or hook - anon key is public by design
const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_ANON_KEY // controlled by RLS policies
);

// supabase/functions/admin-task/index.ts - service_role only here
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
const adminClient = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")! // server-side only
);

The anon key is safe to use in React because Supabase RLS policies control what it can access. Enable RLS on every table and write policies that match your auth model.

If you rotated your Supabase service_role key, generate a new one in the Supabase dashboard under Project Settings > API. Update the Supabase Secret and any server-side code that uses it before revoking the old key. Admin operations that use the old key will break until you update them.

Step 6: Prevent Future Leaks

Pre-commit Hook with Gitleaks

Gitleaks scans your staged files for secrets before each commit:

# Install gitleaks (macOS)
brew install gitleaks

# Test your current repo
gitleaks detect --source . --verbose

# Add pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
gitleaks protect --staged --verbose
if [ $? -ne 0 ]; then
  echo "Gitleaks found potential secrets. Commit blocked."
  exit 1
fi
EOF
chmod +x .git/hooks/pre-commit

Where Secrets Go in a Lovable App

Secret typeWhere it livesAccessed via
OpenAI / Anthropic keySupabase SecretsDeno.env.get("OPENAI_API_KEY") in Edge Function
Stripe secret keySupabase SecretsDeno.env.get("STRIPE_SECRET_KEY") in Edge Function
Supabase anon keyLovable env as VITE_SUPABASE_ANON_KEYimport.meta.env.VITE_SUPABASE_ANON_KEY (public)
Supabase service_roleSupabase Secrets (no VITE_ prefix)Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") in Edge Function only
Database URLSupabase Secrets (no VITE_ prefix)Deno.env.get("SUPABASE_URL") in Edge Function only

How do I know if my Lovable app has an exposed API key?

Open your deployed Lovable app in the browser, press F12, go to Sources, and search for your key value or its prefix (sk_proj_, sk_live_, AKIA, or eyJhbGci for Supabase JWTs). If it appears in a .js file, it is in your client bundle. Also run grep -r 'VITE_' . in your Lovable project's downloaded code to catch prefix-exposed variables.

What is the Supabase service_role key and why is it dangerous in Lovable apps?

The service_role key bypasses all Row Level Security policies, giving full read and write access to every table in your Supabase database. Lovable's AI occasionally generates code that uses this key in React components. Any visitor who opens DevTools and copies it has complete database access. Use the anon key in React components and add RLS policies instead.

Where should I store API keys in a Lovable app?

Store secrets in Supabase under Project Settings > Edge Functions > Secrets. Access them in Edge Functions as Deno.env.get('YOUR_SECRET'). The VITE_SUPABASE_ANON_KEY and VITE_SUPABASE_URL are safe to expose publicly because Supabase designs them for browser code, protected by RLS. Everything else belongs server-side.

Does Lovable's hosted deployment protect my environment variables from the bundle?

Only if they are stored as Supabase Secrets and accessed in Edge Functions. Variables with a VITE_ prefix are still baked into the client JavaScript by Vite at build time, regardless of where the app is hosted. The prefix is the deciding factor, not the hosting platform.

Do I need to rotate my key if I am not sure it was accessed?

Yes. Rotate it regardless. Automated scanners index public repositories within minutes of a push. If the key was ever in a public GitHub repo or visible in your deployed app's JS bundle, assume it was found. Check the service's usage dashboard for unexpected spikes before revoking.

Check your Lovable deployment for API keys in your JavaScript bundle, Supabase service_role misuse, and security header gaps. Free scan, no signup required.

How-To Guides

How to Fix Lovable API Key Exposure (2026)