Supabase anon key: what it can access
The Supabase anon key is meant to be public, and exposing it is safe only if every table is protected by Row Level Security (RLS). It ships in every browser bundle that talks to Supabase. Policies and database grants protect your data, not the key. If a table in an exposed schema has RLS disabled, anyone holding the anon key can read and write it.
What is the Supabase anon key?
The anon key is a legacy API key, a JWT signed by your project with the role anon. Supabase’s newer equivalent is the publishable key, sb_publishable_.... Both identify your app to the API and let requests run as the anon role until a user signs in. See the Supabase API-key guide.
| Key | Where it belongs | What protects your data |
|---|---|---|
Legacy anon key |
Browser, mobile app | RLS policies and grants |
Publishable key, sb_publishable_... |
Browser, mobile app | RLS policies and grants |
Legacy service_role key |
Backend only | Nothing: it bypasses RLS |
Secret key, sb_secret_... |
Backend only | Nothing: it bypasses RLS |
What can someone do with my anon key?
Anything the anon role is allowed to do. In practice that means:
- Read and write tables without RLS. The Data API exposes tables in the
publicschema by default. RLS disabled means no row filter at all. - Read tables with permissive policies. A policy of
using (true)grants every row to every caller it applies to. - Sign up. If email signup is enabled, anyone can create an account and get the
authenticatedrole. A policy writtento authenticated using (true)is then effectively public. - Call database functions in exposed schemas that the role has
EXECUTEon, includingsecurity definerfunctions that run with their owner’s rights. - List and download files from public storage buckets, or from private buckets whose storage policies allow
anon.
Our anon-key honeypot study records what callers did with a deliberately exposed key.
How to test what your anon key can reach
Use the key your deployed frontend ships, not a privileged one. Request one row from each table:
curl "https://<project-ref>.supabase.co/rest/v1/<table>?select=*&limit=1" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"
A JSON row means anonymous callers can read that table. An empty array [] means RLS filtered every row, or the table is empty. A permission error means the role has no grant on it.
Then repeat the request signed in as an ordinary test user, and try POST, PATCH and DELETE against rows that belong to a second test user. The Supabase RLS checker runs these checks against a deployed app.
Supabase’s Security Advisor in the dashboard also flags tables in exposed schemas with RLS disabled.
Should I rotate a leaked anon key?
Rotating the anon key does not fix an exposure. The key was public before and the replacement will be public too. If someone reached data they should not have, the fix is the policy that let them, not the key.
Rotate only if the value you exposed was actually the service_role or a secret key. The service-role exposure walkthrough covers that case.
Fix checklist
- Enable RLS on every table in an exposed schema.
- Replace
using (true)policies with checks onauth.uid()or a membership table. - Disable signup if the app has a fixed set of users.
- Revoke
EXECUTEfrompublicandanonon functions the browser does not need. Postgres grants it topublicby default, so revoking fromanonalone changes nothing. - Make storage buckets private unless the files are meant for anyone.
The Supabase RLS guide shows policy patterns for each case, and the Supabase hardening guide covers the rest of the project configuration.
Reviewed against the Supabase docs on October 9, 2026.
See what your anon key can reach
We call your Supabase project with the key your frontend already ships and report every table, bucket and function it can touch.
14-day free trial · No credit card · Cancel anytime