Supabase RLS Not Working? Fix It by Symptom
You enabled RLS. The policy says auth.uid() = user_id. But select * from orders still returns every row.
The policy is syntactically correct. The dashboard shows "RLS Enabled: Yes."
Why is it leaking?
The 4 Silent Failure Modes
I've audited 50+ vibe-coded Supabase apps (Cursor, Lovable, Bolt, v0). The same 4 patterns cause 90% of RLS leaks:
| Symptom | Root Cause | The Fix |
|---|---|---|
SELECT returns all rows |
Policy only on INSERT/UPDATE — no SELECT policy |
Add FOR SELECT USING (auth.uid() = user_id)
|
| Works in dashboard, fails in app | Service role key used client-side — bypasses all RLS | Rotate key, move to Edge Function |
| Multi-tenant: sees other orgs' data |
Missing org_id in policy — only checks user_id
|
Add AND org_id = current_org_id()
|
| Works locally, leaks on Vercel |
Middleware doesn't forward cookies — getUser() returns null |
Add supabase.auth.getSession() to middleware |
Why This Happens
Vibe coders ship auth + RLS in 10 minutes.
The trap: RLS policies are SQL. AI writes SQL that looks right but misses the FOR SELECT clause, or uses current_user instead of auth.uid(), or forgets tenant isolation.
Senior devs don't ship without verifying:
EXPLAIN ANALYZE SELECT * FROM orders;
-- Check: "Rows Removed by Filter" > 0
If that number is 0, your policy isn't filtering.
The Verification Protocol: Ownership Policy Pattern
Every table in your app needs this pattern:
-- 1. Enable RLS
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- 2. Owner can do everything
CREATE POLICY "owner_all" ON orders
FOR ALL USING (auth.uid() = user_id);
-- 3. Org members can read (multi-tenant)
CREATE POLICY "org_read" ON orders
FOR SELECT USING (org_id = current_org_id());
Critical rules:
-
FOR ALLcovers SELECT/INSERT/UPDATE/DELETE - Use
auth.uid()— nevercurrent_userorsession_user - Multi-tenant tables MUST have
org_idchecks - No
USING (true)orWITH CHECK (true)— ever
The 47 Automated Checks
Our open-source demo runs these on every migration:
- Every table has RLS enabled
- Every table has
FOR SELECTpolicy - Every policy uses
auth.uid()notcurrent_user - Multi-tenant tables have
org_idchecks - No
USING (true)orWITH CHECK (true) - Service role key not in client bundle
- Middleware forwards cookies correctly ...and 40 more
Free Tool: Supabase RLS Leak Demo
Clone, run, see your own leaks in 30 seconds:
git clone https://github.com/cekuu35/supabase-rls-leak-demo
cd supabase-rls-leak-demo
npm install
npm run dev
# Open http://localhost:3000
It connects to your Supabase project, runs the 47 checks, and shows exactly which tables leak what data.
What's Next
Free: Run the demo → see your leaks
$29 Kit: 47-check checklist + remediation scripts
$50/mo Guard: Automated CI gate + drift detection
Comments Welcome
Hit this in your app? Drop the symptom below — I'll add it to the diagnostic framework.
Cross-posted from my dev.to
Top comments (0)