Free scan

Supabase RLS mistakes in AI-built apps, and how to fix them

Updated 16 October 2026 · ShipMender guides

Supabase is the default backend for many vibe-coded apps, and Row Level Security (RLS) is what stops one user reading another user's data. Here are the mistakes we look for, and how to fix each.

Why RLS matters so much in Supabase

Your Supabase URL and anon key are in your front-end code by design, so anyone can find them. That's fine only because RLS decides what that key can do. If RLS is off or too loose, anyone with your site open in a browser can query your tables directly, bypassing your app's interface entirely.

Mistake 1: RLS turned off

Tables created through SQL or migrations may not have RLS enabled. With RLS off, the anon key can read every row and, depending on grants, insert, update or delete.

alter table public.projects enable row level security;

Mistake 2: "Enable for all users" policies

To get past an error, AI tools sometimes add a policy like using (true). That switches RLS on in name only.

Mistake 3: Checking login, not ownership

A policy such as using (auth.role() = 'authenticated') lets any signed-up user read every row. Anyone can sign up, so this is nearly as open as no policy. Check ownership instead:

create policy "Users read own projects"
on public.projects for select
using (auth.uid() = user_id);

Mistake 4: Forgetting insert and update checks

A select policy alone doesn't protect writes. Add with check (auth.uid() = user_id) on insert and update so users can't create or move rows into someone else's account.

Mistake 5: Using the service_role key in the browser

The service_role key bypasses RLS completely. If it's in client code, your policies don't matter. Move it server-side and rotate it. See leaked API keys in Lovable and Bolt apps.

Mistake 6: Public storage buckets and views

Storage buckets have their own policies, and views can bypass the RLS of underlying tables unless created with security_invoker. Check both.

How to check your project

  • In the Supabase dashboard, the Table Editor and the Security Advisor flag tables without RLS.
  • Sign in as a test user and try reading another test user's rows through the API.
  • Search your code for service_role.

Prompt to fix it

Enable Row Level Security on every table in the public schema. For each table with a user_id column, add select, insert, update and delete policies that only allow rows where auth.uid() = user_id. Remove any policy using (true). Show me the SQL before running it.

Check your app in a couple of minutes

ShipMender scans your public GitHub repo or live URL for the issues in this guide: leaked keys, missing Supabase RLS, unprotected routes, open CORS, unverified Stripe webhooks and vulnerable packages. The teaser is free; the full report with copy-paste fix prompts is £29.

Run a free scan