BuiltSafely

Home/Fixes

Supabase

How to check if a secret key is in my frontend bundle

Build your app, then search the output for secret keys and service_role tokens. Three tested commands.

  • Reviewed Oct 2026
  • 3 official sources
Sources Last checked October 16, 2026 · Sources: official docs (linked below) · Educational, not an audit

The fix

Build your app, then search the built output (not just your source) for secret keys. Run these from your project folder after a production build. Swap dist for your build folder, for example build or .next/static.

# 1. New-style secret keys (should print nothing)
grep -rnoE 'sb_secret_[A-Za-z0-9_-]+' dist/

# 2. Legacy JWT keys: print the role inside each one ("anon" is fine, "service_role" is not)
grep -rhoE 'eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+' dist/ | cut -d. -f2 | while read p; do
  printf '%s' "$p==" | tr '_-' '/+' | base64 -d 2>/dev/null; echo
done

# 3. Publishable keys (expected and fine)
grep -rnoE 'sb_publishable_[A-Za-z0-9_-]+' dist/

If step 1 prints anything, or step 2 shows "role":"service_role", treat the key as leaked. Move it to server-only code, then rotate it as described below.

Tested October 16, 2026 on a tiny demo app we built for this, with fake keys and no real project. With a fake secret key in a VITE_-prefixed variable, step 1 found it in the built JavaScript. Step 2 decoded a fake legacy key and showed "role":"service_role". Step 3 found the publishable key, as expected. A variable without the VITE_ prefix was not in the bundle. After moving the secret out of the prefixed variables and rebuilding, steps 1 and 2 printed nothing. The base64 step assumes a Linux-style base64 -d; on some systems the flag differs (untested there).

Why it matters

A secret key "bypasses every Row Level Security policy you have", and it must never be in a browser, a shipped app or source control (Supabase: API keys).

Searching variable names isn't enough. In our demo, searching the bundle for SECRET_KEY or service_role found nothing, because bundlers insert the value, not the name. Search for the key formats instead.

How keys end up in a bundle: build tools copy certain environment variables into client code. Vite exposes variables "prefixed with VITE_" (Vite: Env variables), and Next.js does the same for NEXT_PUBLIC_ variables (Next.js: Environment variables). Supabase warns that server-only variables must not get these prefixes, "or your bundler will ship the key" (Supabase: API keys).

If you find one

Supabase's steps (Supabase: API keys):

  1. Fix the cause first.
  2. Create a new secret key.
  3. Replace the old key everywhere it's used.
  4. Confirm every component uses the new key.
  5. Retire the old key.

Also check your git history and CI logs, since a key in either should be treated as exposed (Supabase: API keys).

Related: is the publishable key safe? · how we judge free scanners · our Supabase RLS guide

Your turn

Have you ever found a key somewhere you didn't expect: a bundle, a log, a screenshot? Tell us where. We'll add the common spots to this guide.

This guide is educational and based on the vendor's public docs as of the review date. Platforms change: check the linked sources. Not a security audit. Disclaimer.

The BuiltSafely weekly

One email a week. What changed, and the one fix that matters.

Platform changes that affect the security of AI-built apps, checked against official docs. Free. Unsubscribe any time.