Is the Supabase anon key safe to expose? Yes, if (and only if) RLS is right
Updated 18 October 2026
Short answer: yes, the Supabase anon key (and its newer replacement, the publishable key, sb_publishable_…) is designed to be shipped in your frontend. Supabase's own docs say it's safe to use in a browser as long as Row Level Security is enabled on your tables and your policies are correct. The key isn't the security boundary. RLS is.
What the anon key actually is
The legacy anon key is a JWT whose role claim is anon. When a request arrives with it, Supabase's API runs your query as the Postgres anon role (or as authenticated once a user signs in and their session token is sent). So the key identifies your project, not a privileged user. Anyone can copy it from your site, and that's expected.
What someone can do with your anon key
- Anything the
anonrole is allowed to do by your RLS policies and grants: read, insert, update or delete rows in tables in exposed schemas (usuallypublic). - Call any RPC function the
anonrole can execute. - Sign up users if email sign-up is enabled, and use other Auth endpoints.
- Read files in public storage buckets (and private ones if storage policies allow).
If a table has RLS disabled, the first bullet means everything in that table. That's how most vibe-coded app data leaks happen.
The key you must never expose: service_role / secret
The service_role key (a JWT with "role": "service_role") and the newer secret keys (sb_secret_…) bypass RLS completely. If one appears in your frontend, anyone can read, modify or delete all of your data. Rotate it immediately in Supabase's API settings and move any code that needs it into an Edge Function or server.
How to tell them apart: paste a JWT into a decoder (or base64-decode the middle part) and look at the role claim. anon is fine to expose; service_role is not. ShipCheck does this automatically for every JWT it finds in your HTML and JavaScript bundles.
When the anon key is safe
- RLS is enabled on every table in every exposed schema.
- Policies are scoped (e.g.
auth.uid() = user_id), and anything readable byanonis genuinely meant to be public. - Views use
security_invoker(or aren't exposed), andsecurity definerfunctions are reviewed. - Storage buckets holding private files are private, with policies.
- Abuse protections are on where it matters: CAPTCHA on sign-up, rate limits, and email confirmation.
How to check yours in two minutes
- Run Supabase's Security Advisor (Advisors → Security Advisor) and fix the errors.
- Test what an anonymous visitor can read, table by table. See the Supabase RLS checker guide for the exact curl command.
- Search your live JavaScript for
service_roleandsb_secret_.
Should I hide the anon key anyway?
You can't really: a browser app has to send it to Supabase, so it will always be visible in network requests. Obscuring it adds no protection. If you need to keep a table completely private, don't rely on hiding the key. Lock the table with RLS and access it only from server-side code using the secret key.