DEV Community

Cenk KURTOĞLU
Cenk KURTOĞLU

Posted on

Supabase RLS Not Working? Fix It by Symptom

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
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)