Stop Leaking Secrets: Lovable .env & API Key Hygiene
The most common secret-exposure pattern in Lovable apps is not a sophisticated attack — it is a service_role key or a third-party API secret that ended up in client-side JavaScript, committed to a public GitHub repo, or stored in a NEXT_PUBLIC_ environment variable. Each of these mistakes makes your credentials readable by anyone who visits your site or views your repository. This guide explains exactly how each leak happens in Lovable-generated apps and what to do when one occurs.
By Founder Name · Last verified: 2026-06-23
Does Lovable commit my .env file to GitHub?
Lovable does not commit a .env file directly, but it does push environment variables into your repository if you set them in the Lovable editor without also adding them to your .gitignore and verifying they do not appear in generated code. The more common risk is that Lovable generates code that imports environment variables using NEXT_PUBLIC_ prefixes — which bundles those values directly into your client-side JavaScript build, making them readable in the browser.
Your Supabase anon key appearing in client JS is expected and safe — it is a public key that respects RLS. Your service_role key or any third-party secret key appearing in client JS is not safe and should be treated as a credential that must be rotated immediately. The distinction matters: not every environment variable in your bundle is a leak, but any secret-tier credential is.
How do I get a leaked key out of my repo safely?
The correct order is critical: rotate the key first, then remove it from the codebase, then scrub git history. Reversing this order leaves a window where the old key is still valid while you are working on the repo, and scrubbing history without rotating first gives attackers extra time after they discover the key. Git history scrubbing removes the key from future clones but does not invalidate access for anyone who already cloned the repo.
- Rotate the key immediately: go to the service that issued the key (Supabase, Stripe, OpenAI, etc.) and generate a new credential. Mark the old key as revoked. Do this before touching the repo.
- Update every deployment environment with the new key: your hosting provider's environment variables panel, any CI/CD secrets, and the Lovable editor settings.
- Remove the key from the codebase: delete or replace it in every file where it appears, commit that change, and push.
- Scrub git history with git-filter-repo or BFG Repo Cleaner to remove the key from all past commits. Force-push the rewritten history.
- Verify: run 'git log --all -p | grep -i <first-8-chars-of-old-key>' to confirm the old key no longer appears in any commit. Also check open pull requests — they may still contain the old key in their diff.
Where should secrets actually live in a Lovable app?
The correct home for secret credentials is a server-side execution environment where they are never sent to the browser. In the Supabase ecosystem that means Edge Functions — server-side TypeScript running on Deno, with secrets injected at runtime, never bundled into client JS. Any credential that must run in the browser is by definition a public key, not a secret key.
The rule of thumb: if a credential allows write access, admin access, or bypasses any access control layer, it must only run in a server-side context. For Supabase specifically, use the anon key in createClient() on the client side. Use the service_role key only in Supabase Edge Functions, and access it via Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') — never import it from a shared module that is also used client-side.
What is the difference between the anon key and the service_role key?
The anon key is the public client key — designed for browser-side JavaScript, it respects all Supabase RLS policies and can only access rows your policies permit. The service_role key bypasses all RLS: it can read, write, and delete any row in any table regardless of policy. It is intended exclusively for trusted server-side code acting on behalf of the system, never a user.
In practice: the service_role key should appear in exactly one place in your codebase — inside Supabase Edge Functions, accessed via Deno.env.get(), never imported as a module-level constant or hardcoded as a string. If you find it anywhere else — in a React component, a Next.js client component, a shared utility, or a NEXT_PUBLIC_ environment variable — treat it as a leaked credential and rotate it immediately.
How do I verify nothing is exposed?
A five-step verification sweep will catch the most common secret exposure patterns in Lovable-generated apps. Run these checks against your codebase before launching to any real users, and repeat them any time you add a new third-party integration or change your Supabase project settings.
- Search for service_role: grep -r 'service_role' src/ — any result that is not inside an edge function is a problem.
- Check NEXT_PUBLIC_ variables: grep -r 'NEXT_PUBLIC_' .env* — confirm none of the values are secret-tier credentials (payment keys, admin keys, signing secrets).
- Check git log for secrets: git log --all -p | grep -iE '(secret|service_role|sk_live|whsec_)' — any result means a key was committed at some point and must be rotated.
- Check the browser bundle: build your app (npm run build) and search the .next/static/ output for your service_role key substring — if it appears there, it is in the client bundle.
- Run a dependency audit: npm audit — while not secret-specific, known vulnerable dependencies can create secondary exposure paths for your secrets.
| Step | What to check | How to verify |
|---|---|---|
| 1. Source grep | service_role key not in any src/ file outside edge functions | grep -r 'service_role' src/ |
| 2. NEXT_PUBLIC_ audit | No secret-tier values in public env vars | grep -r 'NEXT_PUBLIC_' .env* |
| 3. Git history scan | No secrets ever committed to any branch | git log --all -p | grep -iE '(secret|service_role|sk_live|whsec_)' |
| 4. Bundle inspection | service_role key absent from built client JS | grep -r 'service_role' .next/static/ after npm run build |
| 5. Dependency audit | No known vulnerable packages that could expose secrets | npm audit |
Frequently asked questions
Does Lovable commit my .env file to GitHub?
How do I get a leaked key out of my repo safely?
Where should secrets actually live in a Lovable app?
What's the difference between the anon key and the service_role key?
How do I verify nothing is exposed in my Lovable app?
What is a NEXT_PUBLIC_ environment variable and why is it risky?
Can I rotate my Supabase service_role key without breaking my app?
How do I check if my Supabase URL or anon key is safe to expose?
Talk to a senior engineer — not a salesperson.
Book a free 30-minute audit call. We'll diagnose what's wrong and tell you exactly what it costs to fix.