All posts
No-Code & Tools

Is Your Supabase Anon Key Safe to Expose? What Actually Protects Your Data

Seeing a Supabase key in your browser bundle does not automatically mean your app is hacked. Learn which keys are meant to be public, what RLS actually protects, and when exposure is a real emergency.

Elvis Onunwa13 September 20267 min readLast updated: 13 September 2026
Is Your Supabase Anon Key Safe to Expose? What Actually Protects Your Data

TL;DR

  • A Supabase publishable key, and the older anon key it replaces, is designed to be usable in browser code; hiding it is not the security model.
  • Your real protection is correct Row Level Security, least-privilege grants and tested authorization for anonymous and authenticated users.
  • A service_role key or new Supabase secret key must never be shipped to the browser because it has elevated access and can bypass RLS.
  • If you find a privileged key in client code, rotate it, move privileged work server-side and review logs and access immediately.
  • For AI-built apps, do not accept “RLS enabled” as proof of safety: test whether each policy actually denies the wrong user.
On this page⌄

If you inspect a Supabase-powered website and find a project URL plus an anon key or sb_publishable_... key in the browser, that alone is not a security breach.

Supabase is designed to support frontend applications that call its Data API directly. Its current security documentation says the publishable key is safe to expose when Row Level Security (RLS) and access policies are correctly configured. Older projects may still use the legacy anon key for the same public-client role.

The dangerous mistake is assuming the key itself protects your data.

It does not.

The real question is:

What is someone allowed to do after they copy that public key?

That is where RLS, database grants, authentication and your policy design matter.

If you are preparing an AI-built product for real users, also read my AI-built app production checklist. This guide goes deeper on one specific issue that causes unnecessary panic on one side and dangerous complacency on the other.

The short answer: public key does not mean public database

Supabase currently has two key classes that matter for most new projects:

Key type Intended location Safe in browser? What it should mean
Publishable key (sb_publishable_...) Browser, mobile app, public client Yes Client identifies the project; database policies still decide access
Secret key (sb_secret_...) Server, Edge Function, trusted backend No Privileged backend access

Older projects may instead show anon and service_role keys. Supabase is deprecating those legacy keys by the end of 2026 in favour of publishable and secret keys. Its migration guide maps anon to publishable and service_role to secret. See the official Supabase API key migration guide.

The important distinction is authority, not whether a string contains the word “key.”

A publishable key is expected to be recoverable from a public client. A secret key is not.

Why your Supabase publishable or anon key is visible

A browser application has to send something to Supabase so Supabase knows which project should receive the request.

That means a visitor can usually discover the public project URL and publishable key through JavaScript, environment values compiled into the bundle or browser network tools.

Trying to “hide” a publishable key inside minified JavaScript does not meaningfully secure it. If the browser can use the value, the person controlling the browser can eventually inspect it.

Supabase’s own Securing your data documentation is explicit: frontend access is supported when you use the publishable key and correctly configure RLS and least-privilege access policies.

So instead of asking, “Can someone copy my anon key?”, ask:

“What happens when they do?”

What actually protects the data: RLS plus deliberate privileges

Row Level Security lets PostgreSQL decide which rows a request is allowed to read, insert, update or delete.

Imagine a simple SaaS table called projects:

  • User A owns Project A.
  • User B owns Project B.
  • Both use the same public Supabase project key.
  • Their signed-in sessions carry different user identities.

A correct policy can allow User A to read Project A while returning nothing when User A tries to request Project B.

That is the security boundary you want.

The public key is shared. The authorization decision is not.

But “RLS enabled” is not enough evidence. A policy can exist and still be too permissive.

For every exposed table, test questions such as:

  1. Can a logged-out visitor read anything they should not?
  2. Can User A request User B’s row by changing an ID?
  3. Can a normal user write an admin-only field?
  4. Can a user insert a row that claims another user’s ID?
  5. Can someone call an exposed RPC with parameters the interface never offers?
  6. Are storage objects protected with equivalent ownership rules?

If your answer is “the button is hidden,” you have not tested authorization yet.

The bigger mistake: confusing anon with service_role

A legacy service_role key is completely different from an anon key.

Likewise, the newer sb_secret_... key is different from an sb_publishable_... key.

Supabase says secret and service-role credentials must never be exposed in a browser. They belong in trusted server-side components such as backend services or Edge Functions. The reason is straightforward: privileged credentials have elevated authority and can bypass the row-level restrictions you depend on for normal client access.

Supabase’s current API key documentation specifically warns against placing secret keys in web pages, public source code or bundled client applications.

A practical rule:

  • sb_publishable_... in a browser: expected, then audit RLS and grants.
  • legacy anon in a browser: expected, then audit RLS and grants.
  • sb_secret_... in a browser: security incident.
  • legacy service_role in a browser: security incident.

If you exposed a secret or service-role key, what should you do?

Treat the key as compromised rather than simply deleting it from the next build.

A useful response sequence is:

1. Rotate or revoke the privileged credential

Assume anyone who could load the application could have copied it.

Changing the frontend without invalidating the old credential leaves the copied value useful.

2. Move privileged operations behind a trusted backend boundary

If a feature genuinely needs elevated access, execute it in a server-controlled environment after verifying who is making the request and whether they are allowed to perform the action.

That may be a Supabase Edge Function, a Cloudflare Worker, a server route or another backend you control.

3. Review what the key could reach

Check which tables, storage objects, admin APIs and functions were reachable with the credential. Review logs and unusual activity for the exposure window where practical.

4. Re-test the deployed client bundle

Do not assume your local .env structure proves what ships to production. Build the application and inspect what the actual browser receives.

Framework prefixes such as VITE_ and NEXT_PUBLIC_ exist specifically to expose selected environment variables to client code. That is correct for publishable values and wrong for privileged ones.

Why AI-built apps need this check more than a generic “security scan”

AI-assisted builders are very good at making an interface work quickly. They can also make it tempting to solve an authorization problem by reaching for whichever credential makes an error disappear.

That is exactly the wrong acceptance test.

A privileged key can make a broken permission model appear fixed because the request suddenly succeeds. The product works during the demo while the security boundary has quietly disappeared.

When I review an AI-built MVP, I care less about whether the generator wrote the code and more about whether the system can prove the rules the business depends on.

For Supabase, that means checking the relationship between:

  • authentication;
  • table grants;
  • RLS policies;
  • storage policies;
  • server-side privileged operations;
  • admin actions;
  • functions and RPCs;
  • the actual browser bundle.

The goal is not to remove AI from development. It is to keep implementation speed from becoming permission ambiguity.

A five-minute founder check

You do not need to become a database security engineer to ask the right first questions.

Open your project and answer these:

  • Do I know which key the frontend uses?
  • Is it publishable/anon, or privileged secret/service-role?
  • Is RLS enabled on every table reachable through the Data API that contains protected data?
  • Have I tested one user trying to access another user’s records?
  • Have I checked insert and update rules, not only reads?
  • Are privileged operations executed server-side?
  • Do I know what is actually inside the production JavaScript bundle?

If you cannot answer those yet, that is a useful finding. It tells you exactly where the production-readiness work should begin.

Do not rebuild an app just because the anon key is visible

This is where founders can waste money.

Seeing a public Supabase key in source code is not, by itself, a reason to throw away a working Lovable, Codex or custom-built product.

Audit the system first.

If the public key is the correct client key and RLS policies are sound, visibility is expected. If the database policies are weak, repair the policies and test them. If a privileged credential has shipped, rotate it and restructure the privileged flow.

A full rewrite should be a consequence of broader architectural problems, not fear caused by misunderstanding the key model.

That distinction matters because production work is usually about making the right boundaries explicit, not replacing everything that already works.

When to get a human review

Get additional technical review before launch when the app handles things such as:

  • customer records;
  • payments or financial state;
  • multi-tenant data;
  • medical, legal or otherwise sensitive information;
  • role-based admin actions;
  • private documents;
  • business-critical workflows where one user’s action can affect another user’s data.

The more expensive a permission mistake becomes, the less appropriate it is to rely on “the builder says security passed.”

If you have an AI-built MVP and you are unsure whether it needs a targeted fix, production hardening or a deeper rebuild, contact me. I can review the product as a system rather than judging it only by how polished the generated interface looks.

Frequently asked questions

Questions, answered.

Is the Supabase anon key safe to expose?

Yes. Supabase documents the legacy anon key as safe for browser use when Row Level Security and appropriate access policies are correctly configured. The newer publishable key is the intended replacement.

Why can I see my Supabase key in the browser?

Frontend Supabase clients need a project key to call the Data API, so a publishable or legacy anon key can be visible in JavaScript or network requests. The key is not supposed to be the authorization boundary.

What Supabase key must never be exposed?

Never expose a service_role key or the newer secret key in browser code, public repositories, mobile bundles or other untrusted clients. These privileged keys are for controlled backend environments.

Does enabling RLS make my Supabase app secure?

Not by itself. RLS must be enabled on the relevant exposed tables, the policies must correctly restrict rows and operations, grants should be no broader than needed, and denied cases should be tested.

NEED HELP WITH THE BUILD?

Turn what you learned here into a clearer website or MVP.

Share the goal, current stage, and constraints. I’ll help you identify the smallest responsible next step.

Continue reading

Related notes.