Supabase service_role key exposed: how to check and fix
There are two Supabase keys. One is meant to be public. The other bypasses all your security — and they look almost identical.
Supabase issues two keys that look alike — both are long JWTs starting eyJ. One is designed to sit in your frontend. The other must never leave your server. Confusing them is one of the most damaging mistakes in this stack.
Telling them apart
Both are JSON Web Tokens, so you can read the role out of the middle segment. Decode it and look at the role claim: anon is the public one, service_role is the dangerous one.
Decode a key you found
# paste the key in place of THE_KEY echo "THE_KEY" | cut -d. -f2 | base64 -d 2>/dev/null | grep -o '"role":"[^"]*"'
Check whether yours is exposed
One minute
- 1Open your live app and press F12.
- 2Sources tab → search all files (Ctrl+Shift+F) for: service_role
- 3Also search for: SUPABASE_SERVICE — a mis-prefixed env var is the usual cause.
- 4Any hit means the key is in the bundle every visitor downloads.
The most common route in: naming it with a client-exposed prefix. In Vite that is VITE_, in Next.js NEXT_PUBLIC_, in Create React App REACT_APP_. Anything with those prefixes is compiled into the browser bundle by design.
If it is exposed
- 01Rotate it now: Supabase dashboard → Settings → API → reset the service_role key. Do this before fixing the code — the old key works until you do.
- 02Update wherever it was legitimately used (server functions, backend env vars).
- 03Then fix the code path that leaked it, using the prompt below.
- 04Assume the old key was collected. Review your tables for rows that should not be there and check auth logs for unexpected activity.
Find and remove every use of the Supabase service_role key from client code. 1. Search the project for: service_role, SUPABASE_SERVICE_ROLE_KEY, and any client-exposed variable (VITE_*, NEXT_PUBLIC_*, REACT_APP_*) that holds a Supabase key. Decode any long eyJ token you find and report its role claim. 2. Anywhere the service_role key is used in code that runs in the browser, replace it with the anon key and make the operation work through RLS instead. 3. If an operation genuinely requires bypassing RLS (admin tooling, batch jobs), move it into a Supabase Edge Function or server route that reads the key from a server-only environment variable. 4. Confirm no remaining client bundle contains a token whose role claim is service_role. List every place the key appeared. I will rotate it in the dashboard — tell me explicitly that I must, because the old key stays valid until I do.
This is one check out of 40+
Paste your site address and we run the whole list from the outside — leaked keys, open databases, unprotected pages — then hand you one prompt that fixes what we find. Free, about 30 seconds, no signup.
Check my site — free