5 min read

Using Supabase anon key safely — RLS-protected access only

anon key is public BY DESIGN — without RLS it's a skeleton key. Lovable Apr 2026 BOLA showed 10.3% of apps got this wrong.

Supabase anon key ships in your client bundle (intentionally). Without RLS, every table is open to anyone with the URL.

What it is

anon key = anonymous-role JWT used for browser-side requests. Designed to be public; safety depends on RLS policies on every table.

Vulnerable example

-- table without RLS — anon key reads everything
create table orders (id uuid, user_id uuid, total numeric);
-- no alter table enable row level security

Fixed example

-- enable RLS + tenant-scoped policy
alter table orders enable row level security;
create policy "users_read_own_in_tenant" on orders for select
  using (
    auth.uid() = user_id
    and tenant_id = (auth.jwt() ->> 'tenant')::uuid
  );

How Securie catches it

Securie findingcritical
supabase/migrations/0042_orders_rls.sql:14

Using Supabase anon key safely

Supabase RLS specialist scans every migration; flags any table with tenant_id but no RLS as critical.

Suggested fix — ready as a PR
-- enable RLS + tenant-scoped policy
alter table orders enable row level security;
create policy "users_read_own_in_tenant" on orders for select
  using (
    auth.uid() = user_id
    and tenant_id = (auth.jwt() ->> 'tenant')::uuid
  );
Catch this in my repo →Securie scans every PR · ships the fix as a one-click merge · free during early access

Checklist

  • RLS on every table
  • Default-deny baseline
  • Tenant-scoped policies
  • anon key only client-side
  • service_role key server-only

FAQ

Why is anon key public?

It identifies the project + delegates auth to Supabase Auth + RLS. Safety = RLS.

Related guides