Vigia › Help › Supabase RLS disabled

Supabase RLS disabled? How to check and fix it

Published 25 October 2026 · About 6 minutes · For apps built with Lovable, Bolt, Cursor or by hand on Supabase

Short answer: Your Supabase publishable (or anon) key is meant to be public, so it is not what protects your data. Row Level Security is. If RLS is off on a table in an exposed schema, Supabase's own Security Advisor warns: "Anyone with your project URL can read, edit, and delete all data in this table because Row-Level Security is not enabled." Turn RLS on for every such table, add one policy per operation, and test it.

Vigia publishes this page and sells an automated check for this problem (see the end). It is not a security audit or a guarantee.

On this page: Why it matters · How it happens · How to check yourself · How to fix it · Prompts for Lovable, Bolt and Cursor · FAQ · How Vigia checks this · Sources

Why it matters

Supabase's docs: "A table in an exposed schema without RLS is readable and writable by any role with a grant on it. Enable RLS on every table in an exposed schema." Your publishable key is in your site's JavaScript by design, and Supabase lists it as "safe to expose online" because it "only reaches what Row Level Security allows". With RLS off, that limit is gone.

How it happens

How to check yourself

  1. Run the Security Advisor. Supabase dashboard → Advisors → Security Advisor. Look for "RLS disabled in public" (lint 0013), "policy exists RLS disabled" (0007) and "security definer view" (0010).
  2. Check each table. In the Table Editor, every table in public should show RLS as enabled.
  3. Test as a stranger. Open your app in a private window, logged out, and check that private data never loads. For a proper test, follow step 5 below.

How to fix it

  1. Enable RLS on each table: alter table public.<table> enable row level security; Once RLS is on, "no data is accessible through the API when using a publishable key, until you create policies". Expect parts of the app to stop working until step 3.
  2. Set the grants. Grant anon and authenticated only the operations your app needs, and revoke the rest. A missing grant raises error 42501 before any policy runs.
  3. Write one policy per operation (select, insert, update, delete). Logged out, auth.uid() returns null, so write auth.uid() IS NOT NULL AND auth.uid() = user_id. Don't base policies on user_metadata: users can edit it.
  4. Fix views. On Postgres 15+, recreate views with security_invoker = true so they obey the tables' policies. On older versions, revoke access from anon and authenticated or move the view to an unexposed schema.
  5. Test. Add a .sql file under supabase/tests/ that asserts allow and deny for each operation as anon and as authenticated, then run supabase test db. In Supabase's words: "Until the suite passes, you don't know whether the policies do what you intended."
  6. Run the Security Advisor again until it shows no RLS warnings.

Prompts for Lovable, Bolt and Cursor

Enable Row Level Security on every table in the public schema. For each table, revoke grants that anon and authenticated don't need, and write one policy per operation (select, insert, update, delete) so each user only reaches their own rows, with an explicit auth.uid() IS NOT NULL check. Recreate every view with (security_invoker = true). Show me the SQL before running it.

Add tests under supabase/tests/ that assert allow and deny for select, insert, update and delete, for anon and authenticated, on every public table. Run supabase test db and fix every policy that fails.

FAQ

Is it safe to have my Supabase anon or publishable key in the frontend?

Yes, that is what it's for. Supabase lists the publishable key as safe to expose. It is only safe if RLS is on, with correct policies, on every table in an exposed schema.

I enabled RLS and now my app shows no data. Did I break it?

No, that is expected. With RLS on and no policies, the publishable key can't read any rows. Add a policy for each operation your app needs.

I wrote policies. Is that enough?

Not on its own. Policies do nothing while RLS is disabled, and they don't remove grants. Check both, then test with supabase test db.

Do policies apply to the secret or service_role key?

No. Secret keys bypass RLS, so they must never be in the browser. See Supabase service_role key exposed?

What about views?

A view can bypass RLS. On Postgres 15+, create it with security_invoker = true.

How Vigia checks this

Vigia is a read-only check for apps you own. It downloads your home page and up to 6 same-origin JavaScript files linked from it (<script src> and module preloads; scripts from a CDN or subdomain aren't checked). It finds Supabase keys and decodes the legacy JWT ones (long eyJ… strings) to read their role. A publishable or anon key is reported as info, with a reminder that every table needs RLS. A secret or service_role key is reported as critical, with a fix prompt for Cursor, Lovable or Bolt. It asks you to prove you own the site (a token file or meta tag) before it runs the check. Only the browser tab that proved it can run the check, for 24 hours; after that, you prove it again with a new token. It masks every value it shows (first and last 4 characters at most), including the start of an exposed .git file.

Honest limits: Vigia does not test your RLS policies or read your database, so it cannot tell you whether a table is public. Use the Security Advisor and supabase test db for that. A clean report is not a security guarantee.

The free check is available now. Vigia Pro (€12/month or €120/year, VAT included) re-checks up to 3 apps every week and emails you what changed. Run the free check

Sources

Supabase, Postgres, Lovable, Cursor and Bolt are trademarks of their respective owners. Vigia is not affiliated with or endorsed by them.

Last reviewed 25 October 2026 against the official docs listed above.