Skip to content

Can anyone read my Supabase database from the browser?

6 min read

If you built your app with Supabase and an AI agent, there is a specific question worth answering before a single real user shows up: can a stranger read your database directly from their browser? For a lot of vibe-coded apps, the honest answer is yes, and nobody told you.

Why this happens

Supabase gives your app a public "anon" key that ships in the browser. That key is supposed to be public. What protects your data is not the key, it is Row Level Security (RLS): per-table rules that decide which rows each request may read or write. When RLS is off, the anon key can query the table's REST endpoint directly and get everything back. Your app's login screen is irrelevant, because the attacker never uses your app. They call the database's API themselves.

An AI agent building "make users able to save their profile" will happily create the table and the query and never mention that the table is world-readable, because you didn't ask, and it works in the demo either way.

Check it in two minutes

  1. Open your Supabase dashboard, go to Table Editor, and look for the shield icon next to each table. A table with "RLS disabled" is open.

  2. Prove it from the outside. In a terminal, with your project URL and anon key:

    curl "https://YOUR-PROJECT.supabase.co/rest/v1/YOUR_TABLE?select=*" \
      -H "apikey: YOUR_ANON_KEY"
    

    If rows come back, that table is readable by anyone on the internet who has your (public) anon key, which is everyone who opens your site.

The fix

  1. Turn RLS on for every table that holds user data. In the dashboard: Authentication is not enough; each table needs RLS explicitly enabled.
  2. Write a policy per table that scopes rows to their owner, for example "a user may select rows where user_id = auth.uid()." Do the same for insert, update, and delete. Start from deny, then allow exactly what the app needs.
  3. Re-run the curl test with a signed-out (anon) key. It should now return nothing. Then test as a signed-in user and confirm they see only their own rows, never anyone else's.
  4. Repeat for every table, including the ones that feel harmless. A "waitlist" or "feedback" table full of email addresses is a data leak too.

The trap to avoid

Do not "fix" this by moving the query behind an API route while leaving RLS off. The table's public endpoint is still open; you have only hidden one door and left the other unlocked. RLS is the lock. The API route is a convenience.

Where this fits

Public database access is one of the most common things a stranger hits before your first real users, and it is invisible in every demo. It is exactly the kind of gap the free Readiness Report surfaces: your own agent runs the check against your real project and tells you which tables are exposed, before anyone else finds out. If you would rather have the fixes handed to you in order, that is what the Finishing Pass is for.