Why it matters
Supabase apps talk to the database straight from the browser, using a public key that is visible in your page source. That is by design. The thing that makes it safe is row-level security. Turn it off and the public key becomes a key to the whole table.
Step 1: turn it on for the table
In the Supabase dashboard, open the table and enable RLS, or run this in the SQL editor. Replace bookings with your table name.
alter table public.bookings enable row level security;Step 2: add a policy
With RLS on and no policy, nobody can read the table through the API, which usually breaks a screen. Add a policy that lets each signed-in user read only the rows that belong to them. This assumes the table has a user_id column that stores the owner's id.
create policy "Owners can read their bookings"
on public.bookings
for select
to authenticated
using ((select auth.uid()) = user_id);Step 3: cover inserts, updates and deletes
A select policy only covers reading. Add one policy per action you need, each limited to the owner. For inserts, the check goes in with check rather than using.
create policy "Owners can add bookings"
on public.bookings
for insert
to authenticated
with check ((select auth.uid()) = user_id);Mistakes to avoid
- using (true): this lets everyone read the table and is the same as RLS off.
- Policies for the anon role on tables with personal data.
- Turning RLS off to fix a permissions error. Fix the policy instead.
- Views that query a protected table. Create them with security_invoker = true so they respect the caller's policies.
Check that it worked
Sign out of your app and try to load the data, or follow our guide on testing whether your tables are public with your own anon key. If a row comes back without signing in, the policy is still too open.
Common questions
Does turning on RLS break my app?
It can, until you add policies. Turn it on, add the policies your screens need, then test each screen while signed in as a normal user.
Does the service role key respect RLS?
No. The service role or secret key bypasses every policy. That is why it must never be in your frontend.