# Is the Supabase anon key safe to expose?

**Answer:** Yes. The anon key, and the publishable key that replaces it, is built to ship in your frontend: Supabase's API keys docs call the publishable key "Safe to expose online", and anyone can read it from your JavaScript. What protects your data is row level security on each table plus the grants each role holds, so on a table with RLS off that the anon role is granted, anyone holding the key can read, change and delete every row through the Data API.

## Why this matters

The key reaches every visitor's browser as soon as your frontend creates a Supabase client, and Next.js inlines every NEXT_PUBLIC_ value "into any JavaScript sent to the browser". Treating it as a secret does not work, and it does not need to: Supabase says a publishable key "only reaches what Row Level Security allows". The real risk is the opposite assumption, that an app with the key tucked into an env variable is protected. When a table has RLS off and the anon role still holds its default grants, the key works as a read and write pass to that table for anyone who opens the browser dev tools.

## What we do not know yet

- Whether your project still grants new tables to anon by default. Supabase says existing projects do, and that it is changing the platform default so that exposure becomes opt-in; the grants query below shows what your tables hold today.

## How to check what your anon key can reach, and close it

- List the tables in the public schema that have row level security off. Run this in the SQL editor of your live project:

```sql
select tablename
from pg_tables
where schemaname = 'public' and rowsecurity = false;
```

- See what the anon role is granted on each table. Supabase says that on existing projects a new table in public is granted select, insert, update and delete for anon, and that "Adding policies doesn't take those grants back":

```sql
select table_name, string_agg(privilege_type, ', ' order by privilege_type) as anon_privileges
from information_schema.role_table_grants
where table_schema = 'public' and grantee = 'anon'
group by table_name
order by table_name;
```

- A table in both lists is open to anyone with your project URL and the key from your bundle. The Security Advisor describes it as a table where "Anyone with your project URL can read, edit, and delete all data". This is the request from Supabase's API quickstart, and on such a table it returns every row:

```bash
curl 'https://<PROJECT_REF>.supabase.co/rest/v1/profiles?select=*' \
  -H "apikey: <PUBLISHABLE_KEY>"
```

- The same route accepts POST, PATCH and DELETE, so with those grants the key can also add, change and remove rows. No app, no sign-in.

- Close it in a migration: turn RLS on, take back the grants the app does not need, and add a policy per command. This follows the order in Supabase's RLS guide:

```sql
alter table public.profiles enable row level security;

revoke all on table public.profiles from anon, authenticated;
grant select, insert, update, delete on table public.profiles to authenticated;

create policy "Users can view their own profile."
on public.profiles for select
to authenticated
using ( (select auth.uid()) = user_id );
```

- Send the request again. With the anon grants revoked it now fails with a permission error; on a table that keeps its grants but has no policy for anon, it returns an empty list. Then sign in to the app as a second user and confirm it shows none of the first user's rows.

## What else the anon key reaches

Tables are not the only thing behind the key. Check these too.

### Views

Supabase says views "bypass RLS by default because they are usually created with the postgres user". A view in public over a protected table can hand the anon key every row the table hides. On Postgres 15 and later, make the view run with the caller's permissions so the table policies apply:

```sql
alter view public.profiles_public set (security_invoker = true);
```

### Functions called with rpc()

On existing projects new functions in public are granted EXECUTE by default, and Supabase warns that "RLS doesn't apply to functions". A security definer function in an exposed schema "is callable over the Data API with the creator's privileges". Revoke execute on functions only your server calls:

```sql
revoke execute on function public.admin_report() from public, anon, authenticated;
```

### Storage buckets

Files follow the same model: RLS policies on storage.objects decide who can upload and read. A public bucket is readable without any policy, so private files do not belong in one.

### Anonymous sign-ins

If you turn on anonymous sign-ins in Supabase Auth, those users get the authenticated role, not anon, so a policy written to authenticated lets them in too. Supabase says to tell them apart with the is_anonymous claim in the JWT.

## Optional repo check

For AI-built Vercel + Supabase apps, there are two optional checks. The free Supabase RLS checker on this site (linked below) reads migration SQL you paste, in your browser, and lists public tables without RLS and policies that let everyone through. `npx -y viberaven@1.6.3 check` runs the same SQL rules over every migration in the repo and also looks for a service role key in client code; in a run on 2026-10-04 it flagged a table with no RLS as `rls_disabled` and `NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY` in `.env.example` as `service_role_key_in_client_env`. Both read files, not your live project, so run the SQL above against the project too. It is advice, not a gate, and a repository check, not a live database or security test. It exits with code 1 when it finds a blocker and writes a `.viberaven` folder that it does not add to `.gitignore`.

## FAQ

### Is the Supabase anon key a secret?

No. Supabase's docs treat web pages, mobile apps and CLIs as public environments "because anyone can retrieve the key from the source code or build artifacts". Keeping it in an env variable with a public prefix changes nothing about that; RLS and grants are the protection.

### Is NEXT_PUBLIC_SUPABASE_ANON_KEY safe to put in Vercel?

Yes. A NEXT_PUBLIC_ variable is inlined into the JavaScript sent to the browser, which is where the anon or publishable key belongs. The same prefix on a service role or secret key is the leak: Supabase's own .env example says of server keys, "Never prefix these, or your bundler will ship the key."

### Should I rotate the anon key after it was exposed?

Not for being seen; it is meant to be. Fix the tables it can reach. If you want off the anon key anyway, Supabase's path is to create a publishable key, move your clients to it, then deactivate the legacy keys in Settings, API Keys, which can be reversed. The docs deactivate the legacy anon and service_role keys as one step, so move your servers to a secret key first.

### Can someone with my anon key delete my data?

Only where your grants and policies let the anon role delete. On a table with RLS off that still holds the default grants, yes. With RLS on and no delete policy for anon, a delete matches no rows, and with the delete grant revoked it fails with a permission error.

## Related guides

- Supabase anon key vs service role key: what is the difference? https://viberaven.dev/supabase-api-keys-publishable-secret
- How do I find Supabase tables missing RLS in my migrations? https://viberaven.dev/find-supabase-tables-missing-rls
- What does using (true) mean in a Supabase RLS policy, and when is it wrong? https://viberaven.dev/supabase-policy-using-true
- Can anyone call my Supabase RPC functions with the anon key? https://viberaven.dev/supabase-security-definer-function-anon
- Will the Supabase October 30 Data API change break my AI-built app? https://viberaven.dev/supabase-october-30-data-api-grants
- Free Supabase RLS checker https://viberaven.dev/supabase-rls-checker
- Supabase service role key leaked or exposed: how do I rotate it? https://viberaven.dev/supabase-service-role-key-leaked
- Is Supabase safe for production? https://viberaven.dev/is-supabase-safe-for-production

## Sources

Read on 2026-10-04.

- Supabase, API keys: https://supabase.com/docs/guides/getting-started/api-keys
- Supabase, Row Level Security: https://supabase.com/docs/guides/database/postgres/row-level-security
- Supabase, Securing your API: https://supabase.com/docs/guides/api/securing-your-api
- Supabase, Advisors: 0013_rls_disabled_in_public: https://supabase.com/docs/guides/observability/advisors?lint=0013_rls_disabled_in_public
- Supabase, Build an API route in less than 2 minutes: https://supabase.com/docs/guides/api/quickstart
- Supabase, Migrating to new API keys: https://supabase.com/docs/guides/getting-started/migrating-to-new-api-keys
- Supabase, Views: https://supabase.com/docs/guides/database/views
- Supabase, Storage access control: https://supabase.com/docs/guides/storage/security/access-control
- Next.js, Environment variables: https://nextjs.org/docs/app/guides/environment-variables

HTML page: https://viberaven.dev/supabase-anon-key-safe-to-expose
