← Back to blogDATABASE

Why your Supabase RLS rules aren't enough

Mar 3, 2026·7 min read

Row Level Security is good. It's not enough.

Most tutorials walk you through setting up RLS policies that look something like user_id = auth.uid(). That works. Users can only see their own rows. Problem solved.

Except there are a few gaps nobody talks about.

RLS doesn't protect your functions

If you've written Postgres functions or RPC calls, those run with the privileges of the function definition — not the calling user. A function defined as SECURITY DEFINER can bypass RLS entirely. That's sometimes intentional. Often it isn't.

RLS doesn't protect your storage buckets

Files in Supabase Storage have separate policies from your database tables. A lot of developers set up RLS on their documents table but leave the corresponding storage bucket open. Anyone with the URL can download the file.

Your policies might have logic bugs

A policy like user_id = auth.uid() OR is_public = true looks reasonable. But if is_public defaults to null and you're not filtering nulls carefully, you might be exposing rows you didn't intend to.

Service role keys bypass everything

If your service role key leaks — or if you're accidentally using it in frontend code — RLS means nothing. The service role has full database access, always.

The fix isn't to abandon RLS. It's to treat it as one layer of a few, not as the only thing standing between your users' data and everyone else.

Scan your app for these vulnerabilities →

Free · 60 seconds · No account required

Scan for Free