Short answer
The supabase_admin default privileges only apply to objects supabase_admin creates in public, which in practice means extensions installed there. Your own tables get the defaults of postgres, and those you can change. Revoke the postgres defaults, give each new table an explicit grant together with RLS and a policy, and move extensions into the extensions schema. Don’t try to grant yourself supabase_admin: Supabase reserves that membership.
Why the statement fails, and what to change instead
01
The rows belong to supabase_admin, not to you
“These default privileges apply only to objects that supabase_admin creates. Tables and functions that you create get the default privileges of postgres.” Supabase, Can’t change the default privileges of supabase_admin in the public schema
Supabase sets these default privileges when it creates the database. They give anon, authenticated and service_role every privilege on tables, sequences and functions that supabase_admin creates in public. Tables you create in the SQL editor or in migrations are created by postgres, so they get the defaults of postgres instead.
02
No project role can change them, and the workarounds fail
“The SQL Editor doesn't show warnings, so the statement looks successful there.” Supabase, Can’t change the default privileges of supabase_admin in the public schema
A role can only change default privileges for itself and roles it belongs to, and postgres can’t join supabase_admin: grant supabase_admin to postgres fails with "supabase_admin" role memberships are reserved, only superusers can grant them, and set role supabase_admin is denied. Revoking privileges on an object supabase_admin owns, such as PostGIS’s spatial_ref_sys table, finishes but changes nothing. Postgres only prints a warning.
03
Extensions installed in public are where those objects come from
“Supabase creates most extensions as supabase_admin, even when you run create extension as postgres.” Supabase, Can’t change the default privileges of supabase_admin in the public schema
Auth, Storage and Realtime keep their objects in their own schemas. The objects supabase_admin owns in public come from extensions created there, such as PostGIS or pg_trgm, because create extension without a schema lands in public. You can’t enable RLS on those tables either: alter table public.spatial_ref_sys enable row level security fails with must be owner of table spatial_ref_sys. This query lists the extensions in public and whether each one can move. If Supabase’s fuller object query shows objects in public owned by supabase_admin with no extension, Supabase asks you to contact its support with the result:
select extname, extversion, extrelocatable as can_move from pg_extension where extnamespace = 'public'::regnamespace;04
Change the defaults of postgres, which you do control
“These statements change only objects that postgres creates after you run them. Existing objects keep their grants.” Supabase, Can’t change the default privileges of supabase_admin in the public schema
This is Supabase’s SQL for removing the default grants to the API roles on objects postgres creates in public. The last statement names no schema, so it covers functions postgres creates anywhere: afterwards, grant execute on each new function to the roles that call it, including helper functions your RLS policies use. If a migration tool creates objects as another role, change that role’s defaults too. Turning off “Automatically expose new tables” in the Data API settings changes the same postgres defaults, but leaves truncate, references, trigger and maintain on tables, and execute on new functions for PUBLIC.
alter default privileges for role postgres in schema public revoke all on tables from anon, authenticated, service_role; alter default privileges for role postgres in schema public revoke all on sequences from anon, authenticated, service_role; alter default privileges for role postgres in schema public revoke all on functions from anon, authenticated, service_role; alter default privileges for role postgres revoke execute on functions from public;05
Then grant each new table on purpose, with RLS and a policy
After that change, the Data API can’t reach a new table until you grant it. A grant on a table without RLS lets that role read and write every row, so put the grant, RLS and a policy in the same migration. For a notes table where each user owns their rows:
alter table public.notes enable row level security; grant select, insert, update, delete on table public.notes to authenticated; create policy "owners manage their notes" on public.notes for all to authenticated using ( (select auth.uid()) = user_id ) with check ( (select auth.uid()) = user_id );06
Move extensions out of public
“To keep supabase_admin from creating objects in public, create extensions in the extensions schema.” Supabase, Can’t change the default privileges of supabase_admin in the public schema
For each extension where can_move is true, alter extension pg_trgm set schema extensions moves it, and its objects keep their grants. Before moving one, add extensions to the search_path of your functions that set their own, and change calls written as public.similarity(a, b) to extensions.similarity(a, b). PostGIS can’t move this way and dropping it drops the columns that depend on it, so Supabase asks you to contact its support for that one. Rerun the Security Advisor afterwards: the Extension in Public finding should be gone.
alter extension pg_trgm set schema extensions; -- in new migrations, always name the schema create extension if not exists pg_trgm with schema extensions;07
Test as real users, not in the SQL editor
The SQL editor runs as postgres, which skips RLS, so a table can look right there and still be open or closed in the app. Simon tests as a signed-in owner, a different signed-in user and a signed-out visitor, through the same API the app uses.
Checked against the vendor’s docs on : Supabase troubleshooting: Can’t change the default privileges of supabase_admin in the public schema; Supabase docs: Securing your API (grant access explicitly); PostgreSQL docs: ALTER DEFAULT PRIVILEGES.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
Related: Supabase select returns an empty array (missing grant) · Supabase RLS enabled but data still exposed · Supabase “permission denied for schema auth” · Free Supabase grants and RLS checker