Is my Supabase anon key exposed? What's safe to show and what isn't
Short answer: seeing your Supabase anon key (or the newer sb_publishable_… key) in your site's JavaScript is normal and expected. Supabase designed it to be public. What protects your data is Row Level Security (RLS): if RLS is off on a table, or a policy is too broad, anyone holding that public key can read (and often change) the table. The key you must never expose is the service_role / sb_secret_… key, because it bypasses RLS entirely.
The two kinds of Supabase key
| Key | Looks like | OK in the browser? |
|---|---|---|
| Publishable key (new) | sb_publishable_… | Yes, with RLS on |
anon key (legacy) | A long JWT starting eyJ… | Yes, with RLS on |
| Secret key (new) | sb_secret_… | Never |
service_role key (legacy) | A long JWT whose payload says "role":"service_role" | Never |
Supabase's own docs say the publishable key "is safe to expose with RLS enabled", and that the older anon key should be treated the same way: it identifies your project but isn't a secret. Secret and service role keys "are never safe to expose because they bypass RLS" (Supabase: Securing your data, API keys).
Why "the key is public" turns into a data leak
Apps built with Lovable, Bolt and similar tools usually talk to Supabase straight from the browser. That's fine as long as the database itself decides who can see which rows. When a table has no RLS, or a policy like using (true) on private data, the browser key becomes a skeleton key.
This isn't theoretical. CVE-2025-48757 describes exactly this pattern in Lovable-generated apps: insufficient RLS let unauthenticated visitors read or write tables. The researchers who reported it scanned 1,645 apps featured on Lovable's site and found 170 with inadequate RLS (researcher statement). Lovable has since added a security scan and reviewer (Lovable blog), but a scan saying "RLS enabled" doesn't tell you the policies are right.
How to check your own project (10 minutes)
- Make sure no secret key ships to the browser. Open your live site, open DevTools → Sources (or View Source on the JS files) and search for
service_roleandsb_secret_. If either shows up, skip to the fix below and rotate the key today. - Check RLS is on for every table. In the Supabase dashboard, go to Database → Tables. Any table in the
publicschema without RLS is reachable through the API by any role that has a grant on it (Supabase RLS docs). - Run the Security Advisor. Supabase's database advisors flag tables with RLS disabled and other common issues.
- Read each policy, not just the switch. Look for
using (true)or policies grantedto anonon tables that hold user data. Those let any visitor read every row. - Test logged out. In a private window, with no session, query a table you expect to be private through your app or the REST API. You should get an empty list or a permission error, never someone's data.
- Test as a second user. Sign in as user B and try to load user A's records by changing an ID in the URL or request. You should get nothing.
Copy-paste fix prompts
Then rotate the secret key in the Supabase dashboard (Project Settings → API keys). Removing it from the code isn't enough, because the old one has been public.
Things RLS doesn't cover
- Storage buckets have their own policies. A public bucket is public, whatever your table policies say.
- Database functions (RPC): RLS doesn't apply to functions themselves, so only grant
EXECUTEto roles that need it and review anySECURITY DEFINERfunction (Securing your API). - Edge Functions need their own auth checks if they do anything sensitive.
FAQ
Is it safe to have my Supabase anon key in my frontend code?
Yes. The anon key (and the newer sb_publishable_ key) is designed to be public. It's only safe because Row Level Security restricts what that key can do, so RLS must be enabled on every table in an exposed schema with policies that match who should see each row.
Which Supabase key must never be public?
The service_role key (legacy) and secret keys starting with sb_secret_. They bypass Row Level Security and give full access to your data. Keep them in server-side code or Edge Functions only, and rotate them immediately if they were ever in client code.
I turned RLS on. Am I safe now?
Not necessarily. With RLS on and no policies, the API returns nothing to the anon and authenticated roles. But a broad policy such as using (true) on a private table opens it right back up. Review each policy and test as a logged-out visitor and as a second user.
How do I check if my Supabase table is publicly readable?
Open a private browser window with no session and request the table through your app or the REST endpoint using only the public key. If you get rows back that should be private, the table needs RLS and tighter policies. The Supabase Security Advisor also flags tables with RLS disabled.
Do I need to rotate my anon key if it was public?
No, it's meant to be public. You do need to rotate any service_role or secret key that was ever exposed, and fix RLS on any table that was readable, because rotating the anon key won't stop someone who already copied your data.
Sources
- Supabase: Securing your data
- Supabase: API keys
- Supabase: Row Level Security
- Supabase: Securing your API
- Supabase: Database advisors
- CVE-2025-48757 (cve.org)
- Researcher statement on CVE-2025-48757
- Lovable: Secure vibe coding
This guide is general information, not a security audit. Platforms change, so check the linked docs for current details. Spotted something out of date? Email hello@milokits.com.