Skip to content
BuildThenTestedSee packages

Patterns

Finding a service-role key in your browser bundle

A non-engineer's walkthrough: search the JavaScript your app ships for Supabase and Stripe secret keys, understand why it matters, and what to do next.

Every web app ships a bundle: the JavaScript files a visitor's browser downloads to run the page. Anything inside those files is public, because the browser has to read them. This post shows you how to search your own bundle for a secret key in about ten minutes, with no coding.

Why a key in the bundle matters

Supabase, the hosted Postgres backend behind many apps built with AI app builders, issues two kinds of key. The publishable key, formerly called anon, is designed to be public and reaches only what Row Level Security allows. Row Level Security (RLS) is the set of per-row access rules on each table. The secret key, formerly called service_role, has the BYPASSRLS property and skips every one of those rules. Supabase's documentation says never to put it in a browser, a shipped app or source control.

Stripe works the same way. Keys starting pk_ are publishable. Keys starting sk_live_ or sk_test_ are secret keys with unrestricted permissions, and Stripe recommends restricted keys, starting rk_, for anything that does not need full access.

So a secret key in the bundle means any visitor can copy it and act as your server: read every row, change any record, or make charges on your account. It is worth knowing that Supabase's own tooling helps here. Tables created in the dashboard get RLS by default, and Security Advisor flags tables without it. None of that protects you if the key that skips RLS is sitting in a public file.

Escape's scan of more than 5,600 publicly deployed apps built with AI app builders found more than 400 exposed secrets (Escape, 2025). The check below tells you whether yours is one of them.

The ten-minute check

  1. Open your live app in Chrome, right-click anywhere and choose Inspect. Developer tools open at the side.
  2. Click the Sources tab. Press Cmd+Option+F on a Mac or Ctrl+Shift+F on Windows. This searches every file the page has loaded, not just the one on screen.
  3. Search for service_role. Then search for sb_secret_. Then sk_live_ and sk_test_.
  4. Search for eyJ. Legacy Supabase keys are JWTs, signed tokens that start with those three letters. A hit is not a problem by itself, because the publishable key is also a JWT. Copy the value and paste it into the API Keys page of your Supabase project settings to see which key it is.
  5. Click the Network tab and reload the page. Click any request whose address contains supabase.co and read the apikey header under Request Headers. Compare it with the keys in your dashboard.

A bad result is any hit on service_role, sb_secret_, sk_live_ or sk_test_, or an apikey header that matches the secret key rather than the publishable one.

If you use Next.js, also look at the names of your environment variables, the settings your hosting platform passes to the app. Any variable prefixed NEXT_PUBLIC_ is copied into the bundle at build time. A secret with that prefix is the same problem with a different cause; the pattern page for NEXT_PUBLIC_ on a secret covers it.

What to do if you find one

Do these in order, and do the first one today.

  1. Rotate the key. In Supabase, open Project Settings, then API Keys, and generate a new secret key, then revoke the old one. In Stripe, open Developers, then API keys, and roll the key. Anyone who loaded the old build may hold a copy, so a rotation is not optional.
  2. Move the work to the server. Whatever the browser was doing with the secret key needs to run in a place the visitor cannot read: a Supabase Edge Function, a Next.js route handler or server action, or your builder's backend functions. The browser then calls that server code, which holds the key.
  3. Put the publishable key back in the browser and confirm the app still works. If something breaks, it was relying on the secret key to skip RLS, which means the table's policies also need attention. Start with RLS disabled on a table.
  4. Redeploy and repeat the search above on the new build.

Ask your builder's chat to make the change if you do not write code yourself. Describe it exactly: "Move the Supabase secret key out of the frontend into an edge function and use the publishable key in the browser." Then run the check again rather than trusting the answer.

The full pattern record, with the code shape it takes and how we test for it, is at Service-role key shipped in the browser bundle. The Supabase stack hub lists the other Supabase defaults worth knowing and the security features Supabase provides.

What to do this week

Patterns in this post

  • Service-role key shipped in the browser bundleSecrets and keys

    Anyone who opens the app can copy the key from the downloaded JavaScript and read, change or delete every row in every table.

    Critical
  • NEXT_PUBLIC_ prefix on a secretSecrets and keys

    The build writes the secret's value into public JavaScript, so anyone who loads the site can read it, and a redeploy with the same variable name republishes it.

    Critical
  • Row Level Security disabled on a tableData access

    Anyone with the project URL and the public anon key, both of which sit in the app's JavaScript, can read, edit or delete every row in the table.

    Critical
  • Third-party API key in the JavaScript bundleSecrets and keys

    Anyone who views the page source can take the key and run up charges, send messages or pull data on your account until the key is revoked.

    High

Questions

how do i check if my supabase service role key is exposed

Open your live app in Chrome, choose Inspect, open the Sources tab and search all files for service_role and sb_secret_. Then check the apikey header on any supabase.co request in the Network tab against the keys in your project settings. A match with the secret key is a bad result.

what happens if the supabase service role key is leaked

Supabase documents the secret key as bypassing every Row Level Security policy, so anyone holding it can read, change or delete every row in every table. Rotate the key in Project Settings, move the code that used it to a server-side function, and use the publishable key in the browser.

is the supabase anon key safe to expose

Yes, by design. Supabase's documentation states publishable keys, formerly anon, are meant to be public and reach only what your RLS policies allow. The risk is in the policies, not the key, so check those with two test accounts.