Supabase service_role key exposed in your app: how to fix it
Critical
The service_role key (a JWT whose role claim is service_role) and the newer sb_secret_ keys bypass Row Level Security entirely. If one is in your JavaScript bundle, anyone can read, change or delete every row in your database.
Why it matters
Supabase is designed so the anon/publishable key can be public and the service_role/secret key never is. AI builders sometimes paste the service key into client code to make a failing query work, which silently removes all protection.
Quick fix
- Rotate now: in the Supabase dashboard go to Project Settings → API keys and roll/revoke the exposed secret or service_role key.
- Remove it from all client code and any VITE_/NEXT_PUBLIC_ env variable.
- Move privileged queries into an Edge Function or server route that reads the key from a server-only secret.
- Review your database for unexpected changes, and check Auth users and Storage for anything you did not create.
Step by step
1. Rotate the key
Supabase dashboard → Project Settings → API Keys. If you use the newer keys, delete the exposed sb_secret_ key and create a new one. If you use legacy JWT keys, rotate the JWT secret (this also invalidates the anon key and signs out users, so update your app's anon key afterwards).
2. Remove it from the front end
Search your code for service_role, SUPABASE_SERVICE_ROLE_KEY and sb_secret_. The browser client must be created with the anon/publishable key only.
3. Do privileged work server-side
Move the call that needs the key to server-side code (a Supabase Edge Function, a Vercel/Netlify function, or a Next.js API route / server action) and store the key as a server-only environment variable. In Vite apps, any variable starting with VITE_ is bundled into the browser; in Next.js, anything starting with NEXT_PUBLIC_ is.
// supabase/functions/admin-task/index.ts (Edge Function)
import { createClient } from "npm:@supabase/supabase-js";
const admin = createClient(Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!);
// verify the caller's JWT and permissions here before using `admin`4. Check for damage
Look at recent rows, Auth users, Storage objects and database logs for anything unexpected. If personal data may have been accessed, check your legal obligations to notify users.
Prompt for your AI builder
Paste this into Lovable, Bolt, v0, Cursor or Replit, then review the changes before deploying.
My Supabase service_role/secret key is exposed in the front-end bundle. Remove every use of it from client-side code and from any VITE_ or NEXT_PUBLIC_ environment variable. Create the browser Supabase client with the anon/publishable key only. Move any operation that needed elevated access into a Supabase Edge Function that reads SUPABASE_SERVICE_ROLE_KEY from Deno.env, verifies the caller's JWT, and checks they are allowed to do the action. List every file you changed.FAQ
I deleted the key from my code. Am I safe now?
No. Once a key has shipped to browsers it should be treated as public. It is in old deployments, caches and anyone's saved copy of your JavaScript. Revoke it at the provider and create a new one; removing it from code only stops future leaks.
Is the anon key also a problem?
No, the anon/publishable key is meant to be public. It is only safe if Row Level Security is enabled with correct policies on every table that the API exposes.