Supabase gives you a Postgres database with a REST API in front of it. Create a table and it's queryable over HTTP immediately. That's the appeal, and it's also the failure mode.
RLS is the security model
The anon key that ships in your frontend is designed to be public. It identifies the project, not the user, and it grants no privileges by itself. What decides who can read what is row-level security policies on each table.
Without RLS enabled, a table is readable by anyone holding the anon key. Which is anyone who opened your site and looked at the network tab.
Check it in the dashboard under Authentication → Policies, or directly:
Anything with user data and rowsecurity false is exposed right now.
Enabling it isn't finishing it
Enabling RLS without policies blocks everything, which usually surfaces as the app breaking. The next step is where mistakes happen, a permissive policy gets added to make things work again and never gets tightened.
Read the policies you already have rather than assuming. A policy using true as its condition is RLS that permits everything.
Policies are also per-operation. Select, insert, update and delete each need their own. Locking down reads while leaving updates open is a common half-fix.
The service role key
Supabase issues two keys. The anon key is public by design. The service role key bypasses RLS entirely and must never reach the browser.
On Next.js that means it goes in a server-only environment variable with no NEXT_PUBLIC_ prefix. Search your deployed bundle for service_role and for the key itself. If it's there, rotate it immediately, anything already shipped is compromised regardless of what you change afterwards.
Why this keeps happening
Lovable, Replit and several other AI builders use Supabase as their default backend. They generate the schema and the frontend together, and RLS is a separate deliberate step that a prompt describing a feature won't produce.
This isn't hypothetical. CVE-2025-48757, rated CVSS 9.3, was assigned after more than 170 production Lovable applications were found with fully readable and writable databases. Anyone with the anon key, which is in the frontend by design, could read, modify or delete any row.
Two patterns dominated the 24-app study: credentials sitting in client-side code, and queries that never checked ownership. A Supabase project puts both in the same place.
Quick audit
List tables where
rowsecurityis falseRead the policies on the rest and look for
trueconditionsConfirm policies exist for insert and update, not only select
Search the deployed bundle for
service_roleTest it. Open a private window, hit your REST endpoint with the anon key, see what comes back
That last one is the only step that tells you the truth rather than what the config says.