VibeRaven

What does security_definer_view mean, and how do I fix it?

By Ohad Krispin, founder of VibeRaven · LinkedIn

security_definer_view is the Supabase Security Advisor error for a view the Data API exposes that runs with its owner's permissions instead of the caller's, so it skips the row level security policies of the tables under it. Postgres views behave that way by default. The fix, on Postgres 15 and later, is alter view ... set (security_invoker = true), so the view applies each caller's policies.

psql output from a Supabase Postgres 17.11 container: through a default view, the anon role reads a row that RLS hides and lint 0010 flags the view. After security_invoker = true, anon reads no rows and the lint returns nothing.

It is one of several Security Advisor warnings. For the others, see what each Supabase Security Advisor warning means.

How to fix security_definer_view, then check the fix

  • Make the view run as the caller. It keeps its name, columns and grants:
    alter view public.todo_titles set (security_invoker = true);
  • Put the option in the migration that defines the view too, the way Supabase's lint docs write it. A later create or replace view without it turns it off again: on the test container, reloptions went from {security_invoker=on} to empty and the lint fired again.
    create or replace view public.todo_titles
    with (security_invoker = on)
    as
    select id, title from public.todos;
  • Check the tables under the view. Each caller now sees only the rows their own policies allow, so a table with RLS on and no policy for that role returns nothing through the view either.
  • Confirm the option is set. reloptions should show security_invoker=true or security_invoker=on:
    select relname, reloptions
    from pg_class
    where oid = 'public.todo_titles'::regclass;
  • Run the Security Advisor again. The view should be gone from the list:
    supabase db advisors --linked --type security

When security_definer_view is intended

The lint's source flags a view only on Postgres 15 and later, in a schema the Data API exposes, when anon or authenticated can select from it and security_invoker is not set.

A view meant to show more than the tables allow

A public leaderboard that counts rows in a private table needs its owner's rights to see those rows. Keep it, select only what every visitor may see, and write down why: Supabase says some findings may be intentional. The Advisor keeps listing it.

A view only your server reads

Revoke it from the browser roles, or move it to a schema the API does not expose. Either takes it out of the lint, and the secret or service role key can still read it.

revoke all on public.todo_titles from anon, authenticated;

Related guides

Sources

Why are Postgres views security definer by default?

Supabase's lint docs call it "an unintuitive default, chosen for backwards compatibility with older Postgres versions". Postgres added security_invoker in version 15. A view without it checks permissions as its owner, usually postgres, which owns the tables and so is not held to their RLS.

Is a view in a private schema flagged?

No. The lint checks only the schemas the Data API exposes, public by default, so a view in a schema that is not listed under Exposed schemas is not reported.