VibeRaven
Is my Supabase anon key safe in the frontend, and what replaces it?
Yes, the anon key is meant to be in your frontend, and so is the publishable key that replaces it. Supabase's API keys docs call publishable keys "Safe to expose online", because what protects your data is row level security on every table, not the key. The key that must never reach the browser is the service_role key, and the secret key that replaces it: Supabase says to use secret keys "Only use in backend components of your app". Supabase is also "deprecating the anon and service_role keys by the end of 2026", so plan the switch to sb_publishable_ and sb_secret_ keys now, and check two things while you do: that every table has row level security, and that no secret or service role key sits in client code or behind a NEXT_PUBLIC_ variable.
Why this matters
AI-built apps get the two keys mixed up in a few repeated ways: a NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY or NEXT_PUBLIC_SUPABASE_SECRET_KEY in the env files, a Supabase client created with the service role key inside a "use client" component, or a key pasted straight into a client file. Next.js inlines every NEXT_PUBLIC_ value "into any JavaScript sent to the browser", so whoever loads the page can read it, and the service role key skips row level security entirely. The opposite mistake is common too: treating the anon key as a secret and assuming that hiding it protects the data. It does not. With the anon or publishable key, the only thing between a visitor and your tables is row level security.
What we do not know yet
- The exact day the legacy anon and service_role keys stop working. Supabase's API keys docs say "by the end of 2026" and give no day, as of 2026-10-02.
- Whether every client library and integration your app uses accepts the new key formats. This page did not test a hosted project switching keys; check each library's current docs before you swap them in production.
How to check your keys and move to publishable and secret keys
- Find every place a privileged key could be. Search the repo, env files included, for service role and secret key names and for a raw sb_secret_ value:
grep -rnE "service_role|SERVICE_ROLE|sb_secret_|SECRET_KEY" . --exclude-dir=node_modules --exclude-dir=.git - Any service role or secret key behind NEXT_PUBLIC_, or read in a file that starts with "use client", is in the browser bundle. Rename the variable without the prefix, read it only in server code (a route handler, a server action, an Edge Function), and treat the old key as leaked.
- Check that every table in the public schema has row level security on, because that is what protects your data from anyone holding the anon or publishable key. Run this in the SQL editor of the live project; every row it returns is a table without row level security:
select schemaname, tablename from pg_tables where schemaname = 'public' and rowsecurity = false; - Create the new keys in the dashboard under Settings, API Keys. Put the publishable key (sb_publishable_) where the anon key was, in the frontend. Put the secret key (sb_secret_) only where the service_role key was used on the server, in environment variables without a public prefix.
- If a service role or secret key was ever in client code, a NEXT_PUBLIC_ variable or a public repository, rotate it. Supabase's API keys docs give the order for a secret key: create a new one, replace the old one everywhere, confirm every component uses the new one, then delete the compromised key, which cannot be undone. A legacy key is deactivated instead, which can be reversed.
- Know the safety net, and do not rely on it. Supabase says a secret key "doesn't work in a browser": it matches on the User-Agent header and returns HTTP 401 Unauthorized. That stops a browser from using a leaked secret key, not a script that sets its own headers, so a leaked key still has to be rotated.
Optional repo check
For AI-built Vercel + Supabase apps, `npx -y viberaven@1.6.1 check` reads the repo for the key mistakes above and names the file and line. On fixture repos on 2026-10-02 it flagged `NEXT_PUBLIC_SUPABASE_SECRET_KEY` in `.env.example` as `service_role_key_in_client_env`, a Supabase client created with `SUPABASE_SERVICE_ROLE_KEY` in a "use client" file as `service_role_key_in_client_code`, and a hardcoded `sb_secret_` value in a "use client" file under the same id, each as a blocker with a fix and a reminder to rotate the key. It reads only files: it cannot see the keys in your Supabase dashboard, the environment variables set in your Vercel project, or whether a leaked key was already rotated. It is advice, not a gate, and a repository check, not a live database or security test. A clean result does not prove the app is secure. It exits with code 1 when it finds a blocker and writes a `.viberaven` folder that it does not add to `.gitignore`.
Related guides
Sources
Is the Supabase anon key a secret?
No. The anon key, and the publishable key that replaces it, are made to ship in your frontend. Supabase's API keys docs say publishable keys are "Safe to expose online". Row level security on each table is what decides what a holder of that key can read or change.
Does switching to the new keys protect data that is already exposed?
No. The publishable key has the same job as the anon key, so a table without row level security is as open with one as with the other. Turn on row level security and add policies first; the key switch does not do it for you.
Can I keep using the anon and service_role keys for now?
Until Supabase retires them. Its API keys docs say it is "deprecating the anon and service_role keys by the end of 2026", with no exact day given as of 2026-10-02, so switching before the deadline is the safer plan.
Is VITE_ the same problem as NEXT_PUBLIC_?
The idea is the same: a prefix that tells the build to put the value in the code sent to the browser. Whatever your framework calls it, a service role or secret key must never have it.