Is your Supabase database exposed? How to check Row Level Security
Open Supabase tables are one of the most common serious issues in AI-built apps. The public key in your frontend can query your database directly, so Row Level Security (RLS) is the only thing standing between a visitor and your users' data.
1. Understand why this matters
Apps built with Lovable, Bolt, v0 or Cursor usually talk to Supabase straight from the browser using the anon key. That key ships in your JavaScript, so anyone can open DevTools, copy it and your project URL, and call the Supabase API themselves. If a table has RLS turned off, they can read every row, and often insert, change or delete rows too.
2. List tables without RLS
In the Supabase dashboard, open the SQL editor and run:
select tablename from pg_tables where schemaname = 'public' and rowsecurity = false;Every table this returns is open to anyone with your public key. The dashboard's Security Advisor also flags these, along with other risky settings.
3. Test it from the outside
To see what a stranger sees, request a table with only the public key (swap in your project URL, table name and anon key):
curl 'https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*&limit=1' \ -H 'apikey: YOUR_ANON_KEY' -H 'Authorization: Bearer YOUR_ANON_KEY'If you get rows back for a table that holds private data, it is exposed. An empty list [] usually means RLS is blocking you, which is what you want for private tables.
4. Turn on RLS and add real policies
Enable RLS on each table:
alter table todos enable row level security;Then add policies that tie rows to the signed-in user. For a table with a user_id column:
create policy "Read own rows" on todos for select using ((select auth.uid()) = user_id); create policy "Insert own rows" on todos for insert with check ((select auth.uid()) = user_id); create policy "Update own rows" on todos for update using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id); create policy "Delete own rows" on todos for delete using ((select auth.uid()) = user_id);Only allow public reads on data that really is public, like published blog posts, and never allow public writes unless you mean it.
5. Look for policies that let everyone in
- Policies with
using (true)orwith check (true)on insert, update or delete let any visitor write. AI tools often add these to make an error go away. - Views run with their owner's rights by default and can skip RLS. On Postgres 15 and later, create them with
with (security_invoker = true), or keep them out of the public schema. - Storage buckets marked public can be read by anyone with the file URL. Keep private uploads in private buckets with policies on
storage.objects. - The
service_rolekey skips RLS completely. It belongs only in server code and never behind aNEXT_PUBLIC_orVITE_prefix.
6. Tell your AI tool the rule
Add this to your project instructions: "Every new Supabase table must enable row level security and get policies scoped to auth.uid(). Never use using (true) on insert, update or delete. Never use the service_role key in client code." Then re-check after big changes, since AI edits can quietly loosen policies.
Quick answers
Is it safe that my Supabase anon key is visible in my frontend?
Yes, the anon key is meant to be public. It is only safe when Row Level Security is enabled on every table in your public schema and each table has policies that limit who can read and write which rows. Without that, anyone who copies the key from your site can query your tables.
Do Lovable, Bolt or Cursor turn on Row Level Security for me?
Sometimes, but not reliably. AI tools often create tables with SQL, where RLS is off until you enable it, or add policies like using (true) that let everyone in. Check every table yourself instead of assuming.
What happens if I enable RLS with no policies?
The table is locked: the anon and logged-in roles can no longer read or write it. Your server code using the service_role key still can. Add policies for the access your app actually needs.
How does Cheqvo check this?
A repo scan reads your SQL migrations and flags tables without RLS and policies that allow everyone. A live-site scan finds the Supabase URL and public key your site ships, then checks whether tables can be read with that key. It is read-only and downloads no data.
Check your app now, free
Paste a GitHub repo or your live app's URL. You get a free score in under a minute, with no signup. The $19 Full Report gives a copy-paste AI fix for each issue found, and you get a full refund if the scan fails.
No signup · read-only · we never keep your code. How we handle your code
No signup. We only read. We never run your code or attack your site. We never keep your code, only your report. Reports are saved so you can track progress over time. Delete anytime on request. See the leaderboard
General guidance. A fast automated check for the most common security mistakes in AI-built apps. Not a substitute for a full security audit. Updated 2026-10-19.
All guides · Home · Terms · Privacy