If you built your app on Lovable.dev and wired it to Supabase, the question of whether the service_role key reaches the client bundle is not a theoretical one. The reader I am writing this for is about to submit, or has already submitted, and just learned that the wrong key in the wrong file can hand every row in the database to any visitor.
Short answer
Yes, if Lovable initialised the Supabase client with the service_role key, or if you stored it under a VITE_ prefix, the key is visible in the JavaScript bundle that ships to every visitor. According to Supabase's official API keys documentation, the key must never appear in a browser, mobile, or desktop bundle, because it carries the Postgres BYPASSRLS attribute and grants unrestricted database access. The fix is to rotate the key first, then move privileged calls server-side.
What you should know
- The
service_rolekey bypasses every RLS policy. Postgres treats the role as superuser and skips policy evaluation entirely. VITE_andNEXT_PUBLIC_variables are inlined at build time. Anything under those prefixes ends up in the static JS that the browser downloads.- An audit of 50 Lovable apps in 2025 by tgoldi on dev.to found that 34% of audited apps exposed the
service_rolekey in their client bundle. - Rotation comes before everything else. Auditing Row Level Security policies while the key still ships in the bundle leaves the window open.
- Lovable's own integration guide states that secrets should live inside Supabase's Edge Function secret manager, never in the client.
Where does Lovable actually put the key?
Lovable.dev compiles its frontend with Vite, which means any environment variable starting with VITE_ is inlined into the JavaScript bundle at build time. According to the Vite documentation on environment variables, this is the documented behaviour and not a bug. Variables without the VITE_ prefix are only available in server-side contexts like Edge Functions.
The failure mode is mechanical. A user adds a VITE_SUPABASE_SERVICE_ROLE_KEY in Lovable's secrets panel, then references it from a hook or a component. Vite obeys, the build succeeds, and the key lands inside assets/index-<hash>.js as a string literal. Anyone who opens the Sources tab in Chrome devtools and presses Ctrl-F can read it.
Lovable's documentation on connecting to Supabase emphasises that secrets belong in the Edge Function secret manager: "these secrets are stored securely in Supabase's Edge Function secret manager for your project. They are encrypted and kept safe on the backend." The platform's preferred pattern keeps the service_role key on the server side. The exposure happens when the developer departs from that pattern, usually because the Lovable AI agent suggested a quick admin write from a client-side hook.
Why is the service_role key uniquely dangerous?
The Supabase documentation is unusually direct about this key. The page on API keys describes it as a JWT with the BYPASSRLS attribute, which "means skipping any and all Row Level Security policies you attach." The same page tells developers: "Never use in a browser, even on localhost," and: "Do not bundle in executables or packages for mobile, desktop or CLI apps."
In practice this means three things. First, every SELECT from any table returns every row, regardless of which user is logged in. Second, every INSERT, UPDATE, and DELETE succeeds without policy checks, including deletes from auth.users. Third, the key has access to the entire schema across every project, so it does not stop at the obvious tables.
The anon key, by contrast, is what Supabase calls a publishable key and is documented as "safe to expose online: web page, mobile or desktop app." It is the one your Lovable bundle is supposed to ship with, paired with RLS policies that scope access by auth.uid() and by tenant. The two keys look alike at a glance, which is why the substitution slips past code review.
How can I check whether my Lovable bundle is leaking it?
Three checks, in order of how quickly they confirm or rule out exposure:
- Open the deployed site in Chrome, press F12, switch to the Sources panel, and search for
service_role. A direct hit means the literal key is in the bundle. Search again foreyJh, which is the standard JWT prefix Supabase uses, in case the variable name was minified away. - Open the Supabase dashboard, go to Project Settings, then API Keys. The page lists the Last Used IP for the
service_rolekey. Mobile carriers, residential ISP ranges, or geographies you do not deploy from are evidence that the key is being used by end-user devices, not your backend. - Open the Network tab during a normal browse session and inspect outgoing requests to
*.supabase.co. TheAuthorizationheader carries the key. If it does not match youranonkey, you are looking at a service_role request being initiated from the browser.
Any one of the three confirms exposure. The first is the fastest from a laptop; the second is the cleanest signal from a cold start; the third gives a usage trail you can hand to the team.
What is the correct fix order if I find it leaked?
Order matters because partial fixes leave windows open. The five steps:
| Step | Action | Why this order |
|---|---|---|
| 1 | Rotate the service_role key in the Supabase dashboard | Closes the open window immediately |
| 2 | Update Edge Functions and backend jobs to the new key | They were using the old value; pause them otherwise |
| 3 | Audit Row Level Security policies for USING (true) rules | Permissive policies often live alongside leaked keys |
| 4 | Move the privileged call into an Edge Function | Removes the original reason the key was on the client |
| 5 | Rebuild and redeploy the Lovable app, then re-scan the bundle | Visible verification beats trust |
The rotation is the only step that actually closes the leak. Steps 2 through 4 are infrastructure work that should happen anyway. Step 5 is the verification a careful developer does before declaring the incident closed.
What about RLS misconfiguration alongside the leak?
The 2025 dev.to audit of 50 Lovable apps found that 89% of apps had RLS disabled on at least one user-data table. That is the dominant failure mode, and the service_role leak (34% of audited apps) frequently rides alongside it. The reason: when the AI agent cannot infer the correct policy, it tends to disable RLS or write a USING (true) policy and move on.
A USING (true) policy lets any authenticated visitor read every row of the table. Functionally that is identical to running with the anon key and no policy. The fix is to write per-resource policies that scope by auth.uid() and by tenant, then test them with the anon key from a logged-out browser and from a second test account that should not see the first account's rows.
For builders shipping to App Store or Google Play through Lovable, the mobile binary is a second surface where the same secrets can leak. PTKD.com (https://ptkd.com) is one platform that scans compiled APK, AAB, and IPA files for hardcoded JWTs, hardcoded Supabase URLs paired with privileged keys, and the SDK versions known to ship with the issues Supabase has published advisories about. The report maps each finding to the relevant OWASP MASVS control.
What to watch out for
Three details that get missed in the panic of a fresh disclosure:
First, rotation does not invalidate JWTs that were issued by the old service_role key inside Supabase Auth flows. Those are separate tokens. If an attacker captured a session JWT before rotation, that session lives for its normal expiry. Force-rotate user sessions through auth.admin.signOut() from a freshly-keyed Edge Function if the threat model warrants it.
Second, the Lovable AI agent will sometimes regenerate the file that originally caused the exposure. Add a CHANGELOG entry and a commented-out marker in the affected file so the next iteration of the AI sees the constraint. Better still, move the entire admin path out of the client tree where the agent cannot reach it.
Third, the service_role key is not the only secret that ends up in Lovable bundles. The same 2025 audit found 22% of apps stored other sensitive credentials under VITE_ or NEXT_PUBLIC_ prefixes, including Stripe secret keys and third-party admin tokens. The audit checklist should sweep every secret name, not just SUPABASE_SERVICE_ROLE_KEY.
Key takeaways
- The Supabase
service_rolekey is visible in your Lovable bundle if it was referenced from client-side code or stored under aVITE_prefix. - Postgres
BYPASSRLSmeans Row Level Security is not a backstop. Rotation is the only action that closes the leak. - The correct order is rotate, update server-side consumers, audit RLS, move the privileged call into an Edge Function, redeploy, then re-scan the bundle.
- For Lovable apps that compile to mobile, scanning the APK or IPA with PTKD.com (https://ptkd.com) before submission catches the same patterns Apple and Google reviewers eventually flag.
- Document the policy review and the affected files in your CHANGELOG so the next AI-generated migration does not silently undo it.




