Milo Kits
Guide

Supabase Security Advisor warnings explained: what to fix first

Fix the ERROR findings first, especially "Table publicly accessible" (RLS is off), "View bypasses row-level security" and "Security policy relies on user-editable data": those can let strangers read or change data through your public API. Then check the warnings that quietly undo RLS without turning it off, such as policies that are always true (USING (true)) and SECURITY DEFINER functions anyone can call. A clean Security Advisor is a good sign, but it can't tell whether each policy matches who should see what in your app, so test as a logged-out visitor too.

Where to find the Security Advisor

In the Supabase dashboard, open Security Advisor in your project. You can also get the same list from the CLI with supabase db advisors, or through the Supabase MCP server's get_advisors tool (type security), which means an AI coder connected to your project can read it too (Supabase docs).

Each finding has a level (ERROR, WARN or INFO) and a lint name such as security_definer_view. The names below use Supabase's own summaries, so the wording in your dashboard may differ slightly. Supabase notes that some findings can be intentional, so check each one against what you meant before changing anything.

Fix these first: findings that can leak data

FindingWhat it meansUsual fix
Table publicly accessible
rls_disabled_in_public · ERROR
A table in the public schema has Row Level Security off. Supabase says anyone with your project URL can then read, edit and delete every row.Turn RLS on, then add policies. With RLS on and no policy, the API returns nothing, which is the safe default.
Sensitive data publicly accessible
sensitive_columns_exposed · ERROR
The same open table, but with columns whose names suggest secrets or personal data (password, token, api_key, iban, passport_number and similar).Turn RLS on. Ask whether you need to store that column at all, or move it to a separate protected table.
View bypasses row-level security
security_definer_view · ERROR
Postgres views run with their creator's permissions by default, so a view in public can show rows your RLS policies hide.Make the view respect RLS: security_invoker = on (Postgres 15+).
User data exposed through a view
auth_users_exposed · ERROR
A view reads from auth.users (every user's email and sign-in details) and makes it reachable through your API.Use a profiles table filled by a trigger, or a security-invoker view, as Supabase's docs describe.
Security policy relies on user-editable data
rls_references_user_metadata · ERROR
A policy trusts user_metadata, which any signed-in user can change for themselves, for example to is_admin: true.Keep roles somewhere users can't write, such as a roles table only your server updates.
Turn RLS on for one table
alter table public.your_table enable row level security;
Make an existing view respect RLS (Postgres 15+)
alter view public.your_view set (security_invoker = on);

If you use Supabase Queues, also fix Queue exposed without protection (insecure_queue_exposed_in_api) the same way: enable RLS on the queue's table.

Warnings that are often real holes

These don't turn RLS off, but they can make it pointless. A WARN or INFO label doesn't mean "safe to ignore".

Stop a SECURITY DEFINER function being public
revoke execute on function public.your_function(int) from anon, authenticated, public; -- or, if it doesn't need to bypass RLS: alter function public.your_function(int) security invoker;

Lower risk, still worth a minute

Check it yourself in the SQL editor

These queries only read Postgres's own catalog; they don't change anything. Run them in the SQL editor of your project.

Tables in public with RLS off
select tablename from pg_tables where schemaname = 'public' and rowsecurity = false;
Policies that are always true
select tablename, policyname, cmd, roles, qual, with_check from pg_policies where schemaname = 'public' and (qual = 'true' or with_check = 'true');
Views and their options
-- a view without security_invoker=on (or true) here ignores RLS select c.relname as view_name, c.reloptions from pg_class c join pg_namespace n on n.oid = c.relnamespace where n.nspname = 'public' and c.relkind = 'v';
SECURITY DEFINER functions and who owns them
select p.proname, pg_catalog.pg_get_userbyid(p.proowner) as owner from pg_catalog.pg_proc p join pg_catalog.pg_namespace n on n.oid = p.pronamespace where n.nspname = 'public' and p.prosecdef;

What a clean Security Advisor doesn't tell you

The Advisor looks for known risky patterns. It doesn't know your app, so a green list can still hide problems:

So after the Advisor is clean, test from the outside too: open a private window and try to read each table using only the public key. Our Supabase anon key guide shows how. Real cases where weak RLS led to exposed data are in our security tracker.

The 30 October 2026 change, in one paragraph

According to Supabase's changelog, from 30 October 2026 existing projects stop exposing new tables in the public schema to the Data API automatically: each one needs an explicit grant first. Tables you already have keep their current grants and stay reachable, and RLS works exactly as before. So the change protects tables created after that date, not the ones you have now. If your AI tool creates tables, ask it to add the grant, enable RLS and add a policy in the same migration; without the grant, the API returns a "permission denied for table" error (code 42501) with a hint showing the grant to add.

Copy-paste fix prompts

Triage your Advisor list
Here is my Supabase Security Advisor output: [PASTE IT HERE]. For each finding: 1. Explain in one sentence what it would let someone do to my data. 2. Label it fix now, fix soon, or OK if intentional (say why). 3. For the ones to fix, write the SQL migration, but show it to me before running anything. Rules: never disable RLS to make an error go away, never use the service_role or secret key in client code, and keep USING (true) only on tables that are meant to be public and read-only.
Lock down views and SECURITY DEFINER functions
List every view in my public schema and every SECURITY DEFINER function in my project. For each view, set security_invoker = on unless it truly must bypass RLS, and tell me why if so. For each SECURITY DEFINER function, write one sentence on who should be able to call it, then either revoke EXECUTE from anon, authenticated and public, or switch it to SECURITY INVOKER. Finally, show me how to test each one as a logged-out visitor and as a normal signed-in user.
Replace always-true policies
Find every RLS policy in my public schema whose USING or WITH CHECK is true. For each, tell me whether the table holds per-user or private data. Replace those policies with an ownership check such as (select auth.uid()) = user_id, or the correct rule for my app (ask me if unsure). Keep true only for read-only public data, and never for insert, update or delete without a real check. Show me the SQL and a quick test for each table.

FAQ

Is a clean Supabase Security Advisor enough to launch?

It's a good start, not the whole story. The Advisor checks your database setup for known risky patterns. It can't tell whether each policy matches who should see what in your app, whether your frontend ships a secret key, or whether your own API routes check the user. Also test your tables as a logged-out visitor, using only the public key.

What does "Security Definer View" mean in Supabase?

By default, a Postgres view runs with the permissions of the user who created it, so a view in the public schema can return rows your RLS policies would hide. On Postgres 15 and later, run alter view public.your_view set (security_invoker = on); so the view respects the RLS policies of the tables it reads.

Is USING (true) always wrong in an RLS policy?

No. It's fine for data that is meant to be public and read-only, like published blog posts or a product catalogue. It's wrong for per-user data, and for insert, update or delete policies, because it lets everyone with that role read or change every row.

Does Supabase's 30 October 2026 change fix my existing tables?

No. From 30 October 2026, new tables created in the public schema of existing projects aren't reachable through the Data API until you grant access. Tables you already have keep their current grants, so they're only as safe as their RLS setting and policies.

Can I ignore "Function Search Path Mutable"?

It's lower risk than the RLS and view findings, so fix those first. It's still a quick fix: add set search_path = '' to the function and use schema-qualified names such as public.profiles inside it.

Sources

This guide is general information, not a security audit. Platforms change, so check the linked docs for current details. Spotted something out of date? Email hello@milokits.com.

More guides