The short answer
Your Supabase app sends a public key to every visitor. That is normal. What keeps your data private is Row Level Security (RLS). If RLS is off on a table, anyone with your public key can read it. They don't even need to log in. The steps below show you how to check.
Why this matters for AI-built apps
Tools like Lovable, Bolt, Cursor and Replit can build a Supabase app fast. They also create new tables as you go. A new table may not get RLS or the right policies. One missed table can expose user emails, orders or messages.
Check it yourself in 5 steps
- Look for warnings in the Table Editor. Open your Supabase dashboard and go to the Table Editor. A table marked "RLS disabled" or "Unrestricted" can be read by anyone with your public key. Fix those first.
- Run one SQL query. Open the SQL Editor and run the query below. Any table with
rowsecurity = falsehas RLS turned off.select tablename, rowsecurity from pg_tables where schemaname = 'public' order by tablename;
- Read your policies. Go to Authentication, then Policies. A SELECT policy with
using (true)makes every row public. That is fine for a public list, like blog posts. It is not fine for users, orders, messages or anything private. - Test as a stranger and as the wrong user. Open a private window and try to load data that only signed-in users should see. Then make two accounts. While logged in as account A, try to open account B's records. You should get nothing back.
- Make sure your secret key isn't in the browser. In Supabase, open Project Settings, then API Keys. Copy the first few characters of your secret key (the
service_rolekey or a key that starts withsb_secret_). Search your page source and JS files for them. That key skips RLS, so it must only live on a server.
How to fix an open table
- Turn RLS on for every table in the
publicschema. - Add policies that match who should see what. A common rule is
auth.uid() = user_id, so people only see their own rows. - If your secret key was ever in your frontend code, rotate it in Supabase. Then move it to server-side code.
- Run the checks again after each big AI edit. New tables are easy to miss.
Here is a simple example for a profiles table with a user_id column:
alter table public.profiles enable row level security; create policy "Users read their own profile" on public.profiles for select using (auth.uid() = user_id);
Or ask your AI tool to fix it
Paste this into Lovable, Bolt, Cursor or Replit:
Enable Row Level Security on every table in the public schema. Add policies so users can only read and write their own rows. List any table that should stay public and explain why.
Then check its work with the 5 steps above.
What LaunchMedic checks for you
LaunchMedic scans your live app from the outside, like a stranger would. For Supabase, it finds your project and public key in your site's code. It then asks Supabase which tables that key can see, and how many rows each one has. It checks up to 20 tables. It never downloads or saves your rows.
It also flags a service_role or sb_secret_ key found in your public code. Each problem comes with a fix prompt you can paste into your AI tool.
What it can't see: it only tests reading with the public key. It doesn't log in as your users, and it doesn't test writes. So steps 3 and 4 above are still worth doing.
The same scan also checks for leaked API keys, downloadable .env and .git files, SSL and security headers, uptime, and whether your signup, login and checkout pages still load. We only scan sites you prove you own.
Questions
Is my Supabase anon key a secret?
No. It is meant to be public. It ships to every visitor's browser. RLS is what keeps your data safe. The anon key is only a problem when RLS is off or a policy is too loose.
Is the service_role key a secret?
Yes. It skips RLS completely. If it was ever in your website's code, rotate it in Supabase right away and move it to server-side code.
My table has RLS on but no policies. Is that safe?
For reading, yes. With RLS on and no policy, the public key gets no rows back. But your app can't read that table either, so you still need the right policies.
Does LaunchMedic read my data?
No. It only asks Supabase how many rows each table has. It never downloads rows and never saves them.
Does a clean scan mean my app is safe?
No. A clean scan is a good sign, not a promise. Our checks look for common problems and can miss things. Keep testing with two accounts, too.