Hire Lovable Xperts
Security

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.

  1. 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.
  2. Update every deployment environment with the new key: your hosting provider's environment variables panel, any CI/CD secrets, and the Lovable editor settings.
  3. Remove the key from the codebase: delete or replace it in every file where it appears, commit that change, and push.
  4. Scrub git history with git-filter-repo or BFG Repo Cleaner to remove the key from all past commits. Force-push the rewritten history.
  5. 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.
Rotating the key is not optional — removing the key from git history does not help anyone who already cloned or forked your repo before you scrubbed it. Assume the old key is compromised the moment it was committed, and rotate before doing anything else.

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.

Your Supabase URL and anon key are safe to include in client-side code and will appear in your browser's network traffic — this is expected. The only credential that must never reach the client is the service_role key.

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.

  1. Search for service_role: grep -r 'service_role' src/ — any result that is not inside an edge function is a problem.
  2. Check NEXT_PUBLIC_ variables: grep -r 'NEXT_PUBLIC_' .env* — confirm none of the values are secret-tier credentials (payment keys, admin keys, signing secrets).
  3. 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.
  4. 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.
  5. Run a dependency audit: npm audit — while not secret-specific, known vulnerable dependencies can create secondary exposure paths for your secrets.
Secret verification sweep — step by step
StepWhat to checkHow to verify
1. Source grepservice_role key not in any src/ file outside edge functionsgrep -r 'service_role' src/
2. NEXT_PUBLIC_ auditNo secret-tier values in public env varsgrep -r 'NEXT_PUBLIC_' .env*
3. Git history scanNo secrets ever committed to any branchgit log --all -p | grep -iE '(secret|service_role|sk_live|whsec_)'
4. Bundle inspectionservice_role key absent from built client JSgrep -r 'service_role' .next/static/ after npm run build
5. Dependency auditNo known vulnerable packages that could expose secretsnpm audit

Frequently asked questions

Does Lovable commit my .env file to GitHub?
Lovable does not commit a .env file directly, but it can generate code that embeds environment variables in client-side JavaScript — especially if those variables are prefixed with NEXT_PUBLIC_. The Supabase anon key appearing in client JS is expected and safe. Any secret-tier credential (service_role key, Stripe secret, signing secrets) appearing in client JS must be treated as leaked and rotated immediately.
How do I get a leaked key out of my repo safely?
Rotate the key first — go to the issuing service and revoke the old credential before touching the repo. Then remove the key from the codebase and commit the change. Then scrub git history with git-filter-repo or BFG Repo Cleaner. Rotating first is essential: anyone who cloned your repo before the scrub already has the key, and only revocation stops them from using it.
Where should secrets actually live in a Lovable app?
Secrets belong in server-side execution environments — Supabase Edge Functions being the primary option in the Lovable ecosystem. Edge functions access secrets via Deno.env.get() at runtime, never bundling them into client JS. Any credential that must be used browser-side is by definition a public key, not a secret key: use the anon key, the Stripe publishable key, or the equivalent public-tier credential for client-side operations.
What's the difference between the anon key and the service_role key?
The anon key respects RLS and is safe in the browser. The service_role key bypasses all RLS — it can access any row in any table with no restrictions. The service_role key must only appear in server-side code (Supabase Edge Functions, server actions), accessed via environment variable, never hardcoded or imported into client-side modules.
How do I verify nothing is exposed in my Lovable app?
Run the five-step sweep in the section above: grep for 'service_role' in src/, check NEXT_PUBLIC_ variables for secret-tier values, search git log for committed secrets, inspect the built client bundle for service_role substrings, and run npm audit for vulnerable dependencies. These five checks will catch the most common exposure patterns in Lovable-generated apps.
What is a NEXT_PUBLIC_ environment variable and why is it risky?
NEXT_PUBLIC_ is a Next.js convention that tells the build tool to embed the variable's value directly into the compiled client JavaScript bundle. This makes it accessible to any browser that loads your site — including anyone inspecting your network traffic or bundle files. Public-tier keys like the Supabase anon key and Stripe publishable key are correct to put here. Secret-tier keys like the service_role key must never use this prefix.
Can I rotate my Supabase service_role key without breaking my app?
Yes, but you need to update every place that uses the old key before the rotation takes effect. Generate the new key in your Supabase project settings, update it in every Supabase Edge Function environment, update it in your hosting provider's environment variables panel, and verify your edge functions work with the new key before revoking the old one. If any edge function is missed, it will start returning 401 errors until updated.
How do I check if my Supabase URL or anon key is safe to expose?
The Supabase project URL and anon key are designed to be public — Supabase itself publishes this in their documentation. They appear in every client-side request your app makes, and anyone can see them in the browser's network tab. They are safe to expose because the anon key respects RLS policies and cannot access data those policies forbid. The anon key's exposure is only a risk if your RLS policies are missing or incorrect.

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.

Book a free audit call