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 ALL covers SELECT/INSERT/UPDATE/DELETE
  • Use auth.uid() — never current_user or session_user
  • Multi-tenant tables MUST have org_id checks
  • No USING (true) or WITH CHECK (true) — ever

The 47 Automated Checks

Our open-source demo runs these on every migration:

  1. Every table has RLS enabled
  2. Every table has FOR SELECT policy
  3. Every policy uses auth.uid() not current_user
  4. Multi-tenant tables have org_id checks
  5. No USING (true) or WITH CHECK (true)
  6. Service role key not in client bundle
  7. 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