Supahound is in early access. Sign up now; features marked Coming soon switch on as they ship. See what's included

Supahound

Postgres best practices

Views and functions that bypass RLS, and how to lock them down

A Postgres view runs with its owner's rights by default, so in Supabase a view on an RLS table can show every row to anyone. Create views `with (security_invoker = true)` so they obey the caller's RLS. Functions marked `security definer` also run as their owner: keep them in a private schema, set `search_path = ''` and grant execute only to the roles that need it.

Last updated

Why do views leak data?

Views are usually owned by `postgres`, which bypasses RLS. Unless the view uses `security_invoker`, a select on the view returns rows the caller could never read from the table. The Supabase Security Advisor flags these as security definer views.

create view public.my_notes
with (security_invoker = true) as
  select id, title, created_at from public.notes;

-- Fix an existing view
alter view public.my_notes set (security_invoker = true);

When is a security definer function safe?

  • It lives in a schema the API does not expose, for example `private`.
  • It sets `search_path = ''` and uses fully qualified names, so nobody can shadow a table or function.
  • It checks the caller itself, with `auth.uid()`, before it returns data.
  • `execute` is revoked from `public` and `anon` and granted only where needed.
create schema if not exists private;

create function private.my_team_ids()
returns setof uuid
language sql stable security definer
set search_path = ''
as $$
  select team_id from public.memberships where user_id = (select auth.uid());
$$;

revoke execute on function private.my_team_ids() from public, anon;
grant execute on function private.my_team_ids() to authenticated;

What about RPC functions in the public schema?

Every function in an exposed schema is callable through `/rest/v1/rpc/<name>` by anyone with the anon key, unless you revoke execute. Review each one: if it is `security definer`, it must check the caller. If it takes an ID as a parameter, it must check that the caller may use that ID.

Frequently asked questions

Which Postgres version supports security_invoker views?
Postgres 15 and later. Supabase projects run Postgres 15 or newer.
How do I find risky views and functions?
Open the Security Advisor in the Supabase dashboard. It lists security definer views, tables without RLS and functions with a mutable search_path.
Does Supahound check this?
Yes. The free posture scan lists views without security_invoker and newly exposed tables, alongside the Security Advisor findings.

Find out if someone is abusing your app

Connect your Supabase org read-only. The free posture scan runs in minutes. No credit card.