Supabase row level security: a checklist before you launch
What row level security (RLS) does, the mistakes we see most in AI-built apps, and a plain checklist to work through before real users arrive.
Apps built with Lovable and similar tools often use Supabase for their database and sign-in. Supabase lets the browser talk to the database directly, which is fast to build with. It also means the database itself has to decide who can read or change each row. That decision is row level security, or RLS.
Without it, anyone who finds your public project key, which is shipped to every visitor's browser by design, may be able to read or change data they shouldn't.
How RLS works, briefly
When RLS is enabled on a table, every query is denied unless a policy allows it. Policies are rules such as "a signed-in user can read rows where user_id equals their own ID". Enabling RLS with no policies locks the table completely; enabling nothing leaves it open.
Mistakes we see most often
- Tables created without RLS enabled at all.
- Policies that say "true", allowing everyone, left in from testing.
- Insert or update policies that don't check who owns the row, so users can write rows as someone else.
- Admin access decided by a value the user can edit, such as profile metadata, instead of a protected roles table.
- Storage buckets left public when the files inside are private.
- Using the service role key, which bypasses RLS, in code that runs in the browser.
The pre-launch checklist
Work through each table, not just the ones you remember creating.
- RLS is enabled on every table in the public schema.
- Each table has explicit policies for select, insert, update and delete, or deliberately has none.
- Every per-user table has a user_id column and policies that compare it to the signed-in user.
- Insert and update policies check ownership too (WITH CHECK), not only reads.
- Roles live in their own table that users cannot write to, checked through a security-definer function.
- Anonymous access is granted only to data that is genuinely public.
- Storage buckets have policies matching who should see each file.
- The service role key exists only in server-side code and secrets.
- You've tested as two different users and as a signed-out visitor, and each sees only what they should.
Test it the way an outsider would
The most useful check is simple: sign in as one test user and try to read another's data. Then sign out and try again. Supabase's built-in security advisor also flags tables without RLS and other common gaps.
This checklist is a starting point, not a formal security audit or penetration test.
Not sure where your app stands?
A Build Review includes a look at how your data is protected, with a clear list of what to fix first before you launch or grow.

