How to fix Supabase Row Level Security (RLS) disabled on a table
One of your Supabase tables has Row Level Security turned off. The anon key is in your client-side code, which means anyone who views your site source can read — and possibly write — every row. This is the single most common critical finding in AI-built apps. Fix it by enabling RLS on every table, then writing policies that match how your app actually uses the data. Never ship a Supabase app with RLS off on a user-facing table.
Why it matters
Supabase exposes your database over HTTP using the anon key. That key is meant to be public — RLS is what keeps your data safe. With RLS off, the anon key is equivalent to full database access.
How to check
- 01In the Supabase dashboard, go to Database → Tables.
- 02Look for a shield icon next to each table. A grey or crossed-out shield means RLS is off.
- 03Or run: `select tablename, rowsecurity from pg_tables where schemaname = 'public';`
- 04Any `rowsecurity = false` row is a bug.
Or let SafeToShip check it for you in 60 seconds:
How to fix it
Supabase SQL
Enable RLS on every public table, then add policies. Example for a `posts` table with a `user_id` column:
-- Turn on RLS
alter table public.posts enable row level security;
-- Allow users to read their own rows
create policy "users read own posts"
on public.posts for select
using (auth.uid() = user_id);
-- Allow users to insert rows as themselves
create policy "users insert own posts"
on public.posts for insert
with check (auth.uid() = user_id);
-- Allow users to update/delete their own rows
create policy "users update own posts"
on public.posts for update
using (auth.uid() = user_id);
create policy "users delete own posts"
on public.posts for delete
using (auth.uid() = user_id);Public-read tables
If the table is meant to be public (like a blog posts list), you still need RLS on and a permissive read policy.
alter table public.posts enable row level security;
create policy "anyone can read posts"
on public.posts for select
using (true);
-- No insert/update/delete policy means those are blocked for anon.AI prompt
Copy-paste into your AI tool
Paste this prompt into Cursor, Lovable, Bolt, v0, or Claude Code and it will walk through the fix for your specific codebase.
My Supabase project has a table where Row Level Security is disabled. First, list every table in the public schema and tell me which ones have RLS off. For each one: determine from my codebase which users should be able to read, insert, update, and delete rows (look at my fetch calls, check if there is a user_id or owner_id column, check if it is public data like blog posts). Then write the `alter table ... enable row level security;` and matching `create policy` statements. Make policies as restrictive as possible while still letting my app work.FAQ
Frequently asked questions
- Should I disable RLS for faster development?
- No. Write the policies as you build. Every AI-built app I have seen with RLS off in production had someone pull data they should not have. Turn it on from the start and write policies as you add tables.
- Do I need RLS if I only use the service role key?
- The service role key bypasses RLS by design. If your app only ever queries through your own backend using the service role key, you can keep RLS off. But you must be sure the service role key never touches the client — and most AI-built apps use the anon key directly from the browser, which requires RLS.
- How do I test my RLS policies?
- In the Supabase SQL editor, impersonate a user with `set local role authenticated; set local request.jwt.claim.sub = 'USER_ID';` and try your queries. Or use the `supabase-js` client from a test script with a real user's JWT.
Related fix guides
Fix these too
Exposed Supabase service key
The service role key bypasses all security in Supabase. If it is in your client code, an attacker has full database access. Here is how to find and fix it.
Read moreFirebase rules too permissive
Firestore rules that allow unauthenticated reads or writes leave your database open to anyone. Here is how to write rules that actually protect your data.
Read moreHardcoded API key in JS
Any secret in your client bundle is public. Here is how to find them, rotate them, and move the calls server-side.
Read moreLearn the concepts
Glossary
Row Level Security
RLS is a Postgres feature that Supabase uses to enforce per-row access control. With RLS off, the anon key gives anyone full table access.
Read moreSupabase
An open-source backend-as-a-service built on Postgres. Popular with AI tools for its simple API, but RLS misconfigurations are common.
Read moreFree tools
Check this yourself
Platform guides
Building on these platforms?
Supabase security
Supabase gives you a Postgres database in minutes, but the default RLS policies are wide open. Most Supabase apps we scan have at least one table anyone can read.
Read moreLovable security
Lovable makes it easy to ship fast, but AI-generated backends often ship with open Supabase tables and leaked API keys. Scan your Lovable app before your users find out.
Read moreBolt security
Bolt generates full-stack apps in seconds, but speed can leave security gaps. Exposed environment files and missing CORS configuration are common in Bolt projects.
Read moreCursor security
Cursor helps you write code faster with AI, but AI-assisted code can introduce subtle security issues. Missing headers, exposed files, and insecure cookies slip through easily.
Read moreScan your site for this and 50+ other issues
Free scan. Results in 60 seconds. No account required.