Guides · Updated October 15, 2026
Lovable + Supabase security checklist before launch
Lovable builds your app quickly, and Supabase gives it a database, logins, file storage and server functions. Before real users and payments arrive, work through the eight checks below. Each one explains what can go wrong and how you can check it yourself.
A good first stop is the Security Advisor in your Supabase dashboard (the page address ends in /advisors/security). Lovable also runs a quick security scan each time you publish, which checks your database access rules, and you can run a deeper scan from the project’s Security view.
1. Database access rules (RLS)
Your app reads the database from the browser with a public key. The real protection is Row Level Security (RLS), the rules that decide which rows each person may read or change. Anyone with your public key can read a table that the Data API exposes and that has no RLS.
How to check
In the Supabase dashboard, open Authentication, then Policies. Each table should show RLS enabled and policies that match how the app uses it. In the Security Advisor, look for “RLS disabled in public” and “permissive RLS policy”.
What safe looks like
Policies on private data apply to the authenticated role and match the row owner to the logged-in user, for example (select auth.uid()) = user_id. A policy of using (true) shows every row to everyone it covers, which suits only shared data. Step-by-step queries: check RLS on every table.
2. Logins and admin checks
Hiding the admin button from normal users protects nothing, because a person can call the database or a function directly. Keeping the admin flag in user metadata is also risky, since users can edit their own user metadata.
How to check
Log in with a normal test account and type the admin page address straight into the browser. Ask Lovable to show you the policy or function code that enforces the admin check. In the Security Advisor, look for “RLS references user metadata”.
What safe looks like
The admin check runs on the server, in an RLS policy or an edge function, and roles live in a table only admins can change or in app metadata, which users cannot edit.
3. Payments and Stripe webhook signatures
If your app unlocks paid features from a Stripe webhook, check it carefully. A webhook is a message Stripe sends to your app about a payment. Anyone can send a message to that address, so without a signature check a stranger could fake a “payment succeeded” message.
How to check
Search the webhook function code for constructEvent (or constructEventAsync in an edge function), which checks the Stripe-Signature header against your signing secret. Open Edge Functions, then Secrets, in Supabase and confirm the signing secret is stored there, outside the code.
What safe looks like
The signing secret starts with whsec_, lives only in your secrets, and the function rejects any message whose signature does not match. Paid access is granted from the verified webhook, never from the success page a buyer is sent to.
4. API routes and edge functions
Edge functions run on Supabase servers, and some use the secret key, which skips every RLS rule. A function that does not check the caller can hand one user the records of another.
How to check
Ask Lovable to list every edge function, whether each checks the logged-in user, and whether it uses the secret key. In GitHub, search supabase/config.toml for verify_jwt = false. That setting turns off the platform login check, so those functions need their own check of the caller.
What safe looks like
Each function runs as the logged-in user so RLS applies, or, when it uses the secret key, checks the caller itself and filters every query by the caller’s user ID.
5. Keys in the JavaScript and in git history
The Supabase publishable key (starting sb_publishable_, or the older anon key) is designed for the browser. The secret key (starting sb_secret_, or the older service_role key) skips all RLS and must never reach the browser. Stripe and AI provider keys belong on the server too. A key once committed to GitHub stays in the history after you delete the file.
How to check
Open your live app in Chrome, open DevTools and press Control+Shift+F (Command+Option+F on a Mac) to search every loaded file. Search for sb_secret_, sk_live_, and the last ten characters of your service_role key from Settings, then API Keys. GitHub’s code search covers only the default branch, so older commits need a tool that reads the full history.
What safe looks like
Only the publishable (or anon) key appears in the browser. For a leaked key, rotate it in the provider’s dashboard first, then remove it from your code and git history. Full steps: found an API key in your public JavaScript.
6. Storage buckets
Anyone with a link to a file in a public bucket can download it without logging in. That suits logos. It does not suit invoices or private uploads.
How to check
Open Storage in the dashboard and note which buckets are public. Open Policies in the Storage section to see who can upload, read and delete. In the Security Advisor, look for “public bucket allows listing”.
What safe looks like
Private files sit in private buckets, with policies that give each user only their own files. New buckets are private unless you choose otherwise.
7. AI calls with no limit
Every call to OpenAI or another AI provider costs money. A function anyone can call, with no limit per user, can run up a large bill from one script.
How to check
Ask Lovable which functions call an AI provider, whether each requires a login, and whether it limits calls per user. In the OpenAI platform, open Project settings, then Limits, select Edit spend limit under Spend and turn on Enforce a hard limit. A spend alert alone only sends a notice.
What safe looks like
Every AI function requires a login, limits each user, and sits behind a hard spending cap. (Enforcement isn’t instant, so spend can go slightly over the cap.)
8. Public headers
Headers are short instructions your site sends to every browser, such as “load only over HTTPS”. Missing headers make some attacks easier, such as showing your login page inside a frame on a look-alike site.
How to check
In Chrome DevTools, open the Network tab, reload, click the first request and open the Headers tab. Look for Strict-Transport-Security, Content-Security-Policy with frame-ancestors (or X-Frame-Options), and X-Content-Type-Options: nosniff.
What safe looks like
All of these headers appear on your live domain.
A note on Supabase’s October 30 change
From October 30, 2026, new tables in the public schema of existing projects are no longer exposed to the Data API automatically. Existing tables keep their grants. A new table without an explicit grant returns error 42501, “permission denied for table”.
The safe fix is a narrow grant to the authenticated role, with RLS on and a policy for each action you grant:
grant select, insert on public.your_table to authenticated;
alter table public.your_table enable row level security;
create policy "Users read own rows" on public.your_table
for select to authenticated
using ((select auth.uid()) = user_id);
create policy "Users add own rows" on public.your_table
for insert to authenticated
with check ((select auth.uid()) = user_id);Don’t grant everything to anon or turn RLS off to clear the error, because either one can open the table to anyone. More in our guide to the October 30 change.
Run a free check before you launch
You can work through this list by hand, or start with a free check. It looks at what any browser receives from your public URL, such as keys in the JavaScript and missing headers. Connect GitHub, or Supabase with read-only access, for a free full report on the other areas. If you want the issues fixed, it’s $49 one time: tested fixes for what Prodseal can fix automatically, plus manual steps for the rest.
Run my free checkSources: Supabase docs (Security Advisor, API keys, Securing Edge Functions, Storage), Lovable docs (Publish, Security view), Stripe docs (webhook signatures) and OpenAI docs (Spend limits), checked October 15, 2026.