DEV Community

Mubeen Ahmad Khan
Mubeen Ahmad Khan

Posted on

Why Supabase RLS is Slow (and How to Fix It)

If you are seeing query performance degrade after enabling Row Level Security (RLS) on multi-tenant Supabase tables, the culprit is almost always per-row subquery evaluation.

The Problem: Per-Row Evaluation

When you write policies using subqueries:

CREATE POLICY "Tenant isolation" ON orders
FOR ALL USING (
  tenant_id IN (SELECT tenant_id FROM user_tenants WHERE user_id = auth.uid())
);
Enter fullscreen mode Exit fullscreen mode

PostgreSQL re-evaluates that subquery across every single scanned row rather than resolving it once upfront.

The Fix: Custom JWT Claims with InitPlan Wrapping

Inject the tenant_id into the user's JWT metadata on login and read it through a wrapped (SELECT ...) subquery:

CREATE POLICY "Fast tenant isolation" ON orders
FOR ALL USING (
  tenant_id = (SELECT (auth.jwt() -> 'app_metadata' ->> 'tenant_id')::uuid)
);
Enter fullscreen mode Exit fullscreen mode

Important: Wrapping auth.jwt() inside (SELECT ...) is essential. Without the subquery wrapper, PostgreSQL evaluates the JSON claim extraction repeatedly for every row.

For the complete benchmark data and EXPLAIN (ANALYZE, BUFFERS) execution plans, read the full deep dive on my blog:
👉 Read Full Benchmark & Guide on blog.rowistan.com

Top comments (0)