Back to Blog
Deep Dive 6 min read2026-04-15

3 Supabase RLS Mistakes That Leak Your Users' Data

Row Level Security is one of the best features Supabase offers. It lets you define access rules directly at the database level, so even if your application code has a bug, the database itself refuses to return data a user should not see. But RLS is only effective when configured correctly, and we consistently find three patterns in production SaaS apps where RLS is either absent, bypassed, or misconfigured. Each of these mistakes can expose every row in a table to any authenticated user, and in some cases, to unauthenticated visitors.

Mistake number one: RLS is not enabled at all. When you create a new table in the Supabase dashboard, RLS is disabled by default. This means the table is fully accessible to anyone with the anon key, which is a public key embedded in your frontend code. We regularly scan apps where the founders added several tables during development, enabled RLS on the main users table, but forgot to enable it on related tables like orders, messages, or uploads. An attacker who inspects your frontend JavaScript can find the anon key and the Supabase URL, construct a direct API call, and read every row in any unprotected table. The fix is simple: enable RLS on every table immediately after creation, even before you write your first policy.

Mistake number two: using overly broad policies. A common pattern is to write a SELECT policy like "allow all authenticated users to read all rows" with the expression (auth.role() = 'authenticated'). This satisfies the requirement of "RLS is enabled" but does nothing to limit cross-user access. User A can read User B's data freely. The correct policy for most tables is (auth.uid() = user_id), which restricts reads to rows where the user_id column matches the authenticated user. Make sure this column exists on every table that holds user-specific data and that it is set correctly on insert (either by a default value or a trigger, not by client-side code that can be spoofed).

Mistake number three: forgetting to secure the service role key. Supabase provides two keys: the anon key (safe for the browser) and the service role key (bypasses all RLS). If the service role key is exposed, every RLS policy you have written is irrelevant. We find service role keys in frontend bundles, in public git repositories, and in .env files committed to GitHub history. The service role key should only exist in server-side environment variables, never prefixed with NEXT_PUBLIC_, and never committed to version control. Audit your git history with a tool like trufflehog or a Guardian scan to confirm it has never been exposed. If it has, rotate the key immediately in the Supabase dashboard.

Want to check your app for these issues?

Guardian scans your live app and finds these exact problems in under 5 minutes. No install, no CLI, no configuration.

Scan my app free