Anyone can read your Supabase tables

The app works, and a stranger with your site open can read your users, orders or messages straight from the database — without signing in.

Why it happens

A Supabase app talks to its database from the browser with the project's public (anon) key, which every visitor downloads with the page. What stops a visitor reading everything is row level security: a policy on each table saying who may see which rows. A table created without it, or with a policy that lets everyone in, answers anybody who asks with that public key. A builder that creates tables for you can leave one that way, and nothing about the app looks broken while it is.

How to tell

  • Supabase › Table Editor marks the table “RLS disabled” or “Unrestricted”.
  • Supabase › Advisors › Security Advisor lists the table under “RLS Disabled in Public”.
  • Asking the table's REST endpoint with only the anon key returns rows, not an empty list or a permission error.
  • The free health check lists the table among those an anonymous visitor could open — by name and row count, never a row.

The fix

  1. In Supabase › Table Editor, turn on Row Level Security for each table the check named. With it on and no policy yet, only your server can read the table — the app may stop showing that data until the next step.
  2. Add a policy per table saying exactly who may read and change which rows: usually a signed-in person's own rows (auth.uid() = user_id), and nothing at all for tables only your server uses.
  3. Keep the service_role key on the server. It skips row level security entirely; if it is anywhere in the site's code, rotate it now.
  4. Run the free health check again, then sign in as an ordinary user and check the app still shows what it should.

Where to look

The prompt for your builder

Paste this into Lovable, Bolt, Cursor, Claude Code or whatever built the app. Replace anything in capitals. It never asks you to paste a secret into a chat; keys go into your host’s environment, by you.

Prompt for your builder
My app uses Supabase and some tables can be read by anyone with the public anon key. For every table in the public schema, turn on row level security and write policies so each signed-in user can only read and change their own rows, and tables only the server needs are not reachable with the anon key at all. Give me the SQL migration, explain each policy in one line, and list every place in my code that reads a table without a signed-in user so I can check it still works.

How Keelnest catches it

The free health check finds the Supabase project your site names in its code and asks up to eight of its tables — names like users, orders and messages first — whether the public key can read them, reporting names and row counts, never a row. With Supabase connected, Backend Guard reads the project's security advisor, row level security findings included, on every check and shows them on the app's Backend card.

Know the moment this happens.

Keelnest watches your production app every five minutes and tells you in plain English what broke — with the fix. Free for your first app.

Related