Supabase
Supabase anon and publishable keys: is it safe to expose them?
Yes. These keys are public by design. Finding one in a browser bundle does not prove a breach. The question is what a request using that key can actually reach.
Public key in your frontend bundle: expected. Older projects often use NEXT_PUBLIC_SUPABASE_ANON_KEY; newer projects can use an sb_publishable_ key.
Secret or service_role key in your frontend bundle: a real credential exposure. These keys can bypass RLS. Rotate the exposed key and audit what was reachable.
What the public keys actually are
The legacy anon key is a JWT. New sb_publishable_ keys are public API keys, not JWTs. You can use either in a frontend, but neither replaces database authorization. See the Supabase API-key guide for the current formats and roles.
Postgres grants determine whether the request's role may use a table or function. RLS policies then restrict rows for table operations. With RLS enabled and no applicable policy, rows are denied by default. With RLS disabled, a broad grant can expose the table. An overly broad policy can expose rows too.
A quick decision table
| What you found | What it means | Next check |
|---|---|---|
| Publishable or legacy anon key in browser code | Normal client setup | Test anonymous and signed-in access |
| Secret or service-role key in browser code | Credential exposure | Remove, rotate, and investigate |
| Public key plus private data returned anonymously | The data is exposed | Check grants, RLS, views, and functions |
| Public key plus a permission error | Not proof that all roles are safe | Test user A against user B's records |
anon vs service_role
| anon | service_role | |
|---|---|---|
| Subject to RLS | Yes | No, bypasses it entirely |
| Safe in frontend | Yes, by design | Never |
| If it leaks | Not an incident on its own | Rotate immediately |
The question worth asking instead
Not “is my anon key exposed” but “what can my anon key reach”. Those have very different answers, and only the second one can hurt you. The failure we see most often in AI-built apps is a table that works perfectly in development because RLS was never switched on, then ships that way.
Enabling RLS without writing policies denies access by default. If a table still returns private rows to an anonymous request, check whether RLS is off or a policy is broader than intended. Also review views and functions that can expose the data through a different path.
Test with two users, not just one key
In staging, create harmless private records for test users A and B. Request the same record with no session, as A, and as B. Then test the write operations your app supports. A should reach A's record, not B's. B should get the inverse. Keep the HTTP status and a sanitized response as evidence.
A failed anonymous read is not the end of the test. Many authorization bugs appear only when one valid user requests another user's resource. Our RLS test matrix covers the actors and operations to check. See also OWASP's authorization testing guidance.
Test what your anon key can read
VAS can inspect your deployed frontend and probe externally discoverable Supabase surfaces for public exposure. Your first scan is free. Use the two-user test above alongside it to check the authorization rules specific to your app.
Need the key migration details too? The same team maintains a focused guide to the Supabase anon, service_role, publishable and secret keys.
Common questions
Is it safe to expose the Supabase anon key?
Yes. The legacy anon key and newer publishable key are designed for browser clients. They are not secrets, and rotating a public key does not fix a data exposure. Test object grants and Row Level Security policies to learn what anonymous and signed-in users can actually reach.
What is the difference between the anon key and the service_role key?
The anon key carries the anon role and is fully subject to Row Level Security. The service_role key BYPASSES RLS entirely and can read and write every table regardless of policy. The anon key belongs in your frontend. The service_role key must never leave your server. If a service_role key is in your client bundle, treat it as a breach: rotate it immediately and audit access.
Someone found my anon key in my JavaScript bundle. Have I been hacked?
No, not on its own. The legacy anon key is intended to be public. The question is what a request using it can do. If RLS is disabled and the role has a broad grant, or if a policy is too permissive, private rows may be exposed. RLS enabled with no applicable policy denies access by default. Test it rather than assume.
Should I rotate my anon key?
Rotating the anon key rarely accomplishes anything, because the replacement is equally public. If data was readable before rotation it will be readable after, since the exposure came from a missing policy rather than from the key. Rotate the service_role key if it ever leaked. Fix RLS if a table was readable.
How do I check what my anon key can read?
Test the deployed app with no session, then with two separate test users. Check reads and writes on records each user owns. VAS can probe externally discoverable Supabase surfaces; your own two-user tests are needed to verify the full authorization model.
Find out what is readable before someone else does
Point VAS at your live app to look for externally reachable mistakes. Then verify your product-specific access rules with separate test users.