← Back to blog

Aug 26, 2026

Is Your Supabase Database Actually Public? How Row Level Security Really Works

If you built your app with Supabase, this one thing is worth checking today: the anon key sitting in your client-side code is not a secret. It is meant to be visible, it ships in every page load, and anyone can copy it straight out of your browser's dev tools in about ten seconds. That is not a bug. That is how Supabase is designed to work.

What actually protects your data is Row Level Security (RLS), a Postgres feature that decides, row by row, who is allowed to read or write what. If RLS is off on a table, or on but misconfigured, the anon key alone is enough for anyone to read (or write) every row in that table directly, no login required, no app involved at all. They do not need to hack anything. They just need to know the table exists and send a request.

Why this happens so often

RLS is off by default on a new Supabase table. When you are moving fast, especially with an AI coding assistant generating the schema and the queries, it is easy to get a feature fully working (the app can read and write the data it needs) without ever circling back to lock down who else can. The app working correctly and the data being protected are two separate things, and only one of them is obvious from clicking around your own app while logged in as yourself.

How to actually check it

  1. In the Supabase dashboard, go to Authentication → Policies (or Database → Tables and check the RLS toggle on each table). A table with RLS off, or with no policies defined despite RLS being on, is unprotected.
  2. For a more direct check, use your project's REST endpoint (https://<project>.supabase.co/rest/v1/<table>) with only your public anon key and no user session, and see what comes back. If you get real rows back, anyone else can too.
  3. Do this for every table with real user data: not just the obvious ones, the smaller supporting tables (feedback forms, admin flags, internal notes) get forgotten most often.

How Riskline checks it

Riskline's Supabase checker reads your project's actual SQL migration files and looks for the real Postgres statements involved: which tables exist, whether ENABLE ROW LEVEL SECURITY was actually run against them, and what each CREATE POLICY statement actually restricts. It is a deterministic, rule-based check against your real schema, not a guess and not an AI model reading your code and assuming. Firestore-backed apps get the equivalent check against Firestore's own rules syntax.

Want us to check yours? A free scan finds this in under a minute, no card required.