Supabase · public keys

Supabase anon key exposed? Publishable vs service-role keys

Why a Supabase anon or publishable key belongs in browser code, why service-role and secret keys do not, and what a free public-surface scan can classify safely.

Published · Updated · Reviewed by SafeForProd

A Supabase anon or publishable key in browser code is usually expected; a service-role or secret key in browser code is not. The public key identifies the project and works with the user’s authentication state and Row Level Security. Privileged server keys must remain on the backend.

Do not rotate a publishable key merely because a scanner found it in JavaScript. First classify the key using the Supabase dashboard and current provider documentation.

The key classes have different trust assumptions

Supabase’s current key documentation describes publishable keys as suitable for public components, including web pages where users can retrieve the key from source or build artifacts. Legacy projects may use the anon JWT for the same client-side role.

Secret keys and legacy service_role keys are server-side credentials. Supabase documents that service keys can bypass Row Level Security and should never be exposed to customers or used in the browser.

Primary references:

A safe client configuration

This is a normal browser-side pattern when the values are the project URL and a publishable or legacy anon key:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY,
)

Vite will place VITE_ variables in the client bundle. That is acceptable only for values designed to be public. See Vite: Env Variables and Modes.

The security boundary is not “nobody can see the publishable key.” It is the combination of the signed-in user’s JWT, database grants, and RLS policies.

An unsafe client configuration

This hypothetical example puts a privileged server key into public code:

// Unsafe: a privileged server credential is bundled for every visitor.
const admin = createClient(
  import.meta.env.VITE_SUPABASE_URL,
  import.meta.env.VITE_SUPABASE_SERVICE_ROLE_KEY,
)

If owner-side verification confirms that the value is a current service-role or secret key, revoke and rotate it, remove the replacement from all client-prefixed variables, move the privileged operation to a backend or Edge Function, and rebuild the deployed assets.

Do not use a found key against the project to “see if it works.” Verification should happen through the owner’s dashboard and approved source review.

Why the prefix is not enough

Legacy Supabase anon and service-role keys are both JWT-shaped strings. A generic rule that reports every eyJ... token as a database secret will produce false positives. A provider-aware check should consider:

  • where the value appears;
  • the variable name and surrounding client initialization;
  • the provider’s current key format;
  • the declared role when it can be inspected without using the credential;
  • whether owner-side verification is still required.

A public bundle containing a Supabase URL and publishable key is Informational. A privileged-key pattern in public code may be Needs verification until the owner confirms the role. It becomes Confirmed only when deterministic evidence within scope establishes that a privileged credential is public.

A public key does not prove RLS is safe

Finding the correct publishable key avoids a false alarm. It does not validate the database policy.

Supabase says RLS should be enabled on tables in exposed schemas. Policies then determine which rows the anon or authenticated Postgres role can access. Those rules can be correct, missing, or overly broad regardless of whether the browser uses the intended public key.

Therefore:

  • Key class: can often be classified from public context and owner verification.
  • RLS presence and policy logic: require source, migration, or dashboard access.
  • Actual role behavior: requires explicit authorization, verified domain control, approved test accounts, and bounded requests.

What the free scan can tell you

The free scan can identify Supabase configuration in referenced public assets, classify an obvious publishable key as Informational, flag a privileged-looking pattern as Needs verification, and attach the exact redacted location. It does not query a table, call Supabase, decode private user data, or use the key.

See the sample SafeForProd report for an example that separates public evidence from RLS unknowns.

What requires an Authorized source scan or Human security review

The planned Authorized source scan can review client and server initialization, environment-variable use, migrations, grants, policies, and approved role behavior after the account, source, authorization, and domain gates are satisfied.

A Human security review is appropriate when policies encode complex tenant, organization, invitation, admin, or billing rules that need business context.

The vibe coding security checklist places this review in the broader launch sequence.

Limitations

Supabase is evolving from legacy JWT keys toward publishable and secret key formats. Use the current dashboard label and provider documentation as the source of truth. Key classification does not replace a policy review, and a clean free scan does not mean RLS passed.

Revision history

  • 2026-07-20: Initial publication covering publishable/anon versus secret/service-role keys and public-surface limitations.