// security check

Supabase RLS security check: is your database exposed?

Short answer

If Row Level Security (RLS) is not enabled on a Supabase table, anyone who can find your project URL and anon key — both of which ship in your app's public JavaScript — can read, and often write, every row in that table. CodeCheck tests this from the outside in about a minute and gives you copy-paste SQL to fix it.

Check my app — free

A Supabase RLS security check tells you whether strangers can read your database. Supabase exposes your Postgres tables through a public REST API, and the only thing standing between a visitor and your data is Row Level Security (RLS). If RLS is disabled — or enabled with no policies — the API happily returns every row to anyone holding the anon key.

Here's the catch that trips up almost every AI-built app: the anon key is meant to be public. It's embedded in your frontend bundle. So "nobody knows my key" is never true. RLS is the actual lock, and by default new tables created through the dashboard or SQL editor do not have it turned on.

How to tell if your Supabase tables are exposed

You can check manually by opening your app, finding your Supabase URL and anon key in the network tab or JS bundle, and querying a table directly. If you get rows back without signing in, that table is world-readable.

Anyone can run this against an unprotected table
curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*" \
  -H "apikey: YOUR_ANON_KEY"

# Returns every row? RLS is off. That data is public.

The fix: enable RLS and add a policy

Enabling RLS with no policy denies all access by default, which is safe but breaks your app. You then add policies that describe exactly who can see what. The most common pattern scopes each row to its owner:

Run in the Supabase SQL editor
alter table public.profiles enable row level security;

create policy "Users read their own row"
  on public.profiles for select
  using ( (select auth.uid()) = user_id );

create policy "Users update their own row"
  on public.profiles for update
  using ( (select auth.uid()) = user_id );

Common Supabase mistakes CodeCheck flags

RLS is only one of several traps. AI tools scaffold code fast and leave these behind constantly:

  • Tables with RLS disabled entirely — the #1 finding in vibe-coded apps.
  • RLS enabled but with a permissive policy like `using (true)`, which lets everyone in.
  • The service_role key (which bypasses RLS) shipped to the browser by mistake.
  • Views that silently bypass RLS because they weren't created with security_invoker.
  • Storage buckets left public when they hold user uploads.

Supabase pre-launch checklist

  • RLS enabled on every table in the public schema.
  • A policy exists for each of select / insert / update / delete you actually use.
  • No `using (true)` policies on tables with private data.
  • service_role key only ever used server-side, never NEXT_PUBLIC_.
  • Public storage buckets contain only genuinely public files.

Frequently asked questions

Is my Supabase database public by default?

Your Supabase project's REST API is reachable by anyone with the anon key (which is public). Whether your data is exposed depends on RLS. New tables do not have RLS enabled automatically when created via SQL, so it's easy to ship a table that returns every row to the internet.

Is the Supabase anon key safe to expose?

Yes — the anon key is designed to be public and ship in your frontend. It is not a secret. Security comes from Row Level Security policies, not from hiding the key. Never expose the service_role key, which bypasses RLS.

Will enabling RLS break my app?

Enabling RLS with no policies blocks all access, so your app's reads and writes will start failing until you add policies that match how your app uses each table. Add the policies in the same migration and test signed-in and signed-out flows.

Related

See exactly what's exposed — in about a minute.

Paste your link. CodeCheck checks your live app the way a hacker would and hands you the fix in plain English. Free to start, no card.

Check my app — free

Last updated: July 7, 2026