31 October 2026 · 7 min read · BlitzHash Team · Supabase

Is my Supabase leaking? A 5-step stranger's-eye test for AI-built apps

"Is my Supabase leaking?" is a fair question for any founder whose app was built with Lovable, Bolt, Cursor or v0. These apps talk to Supabase straight from the browser, which means everything a stranger needs to start asking your database questions is already sitting in your public page. That's by design, and it's safe when the rules behind it are right. This post shows you how to look at your own app the way a stranger would, in five steps and about 30 minutes, so you can see what's actually exposed instead of guessing.

The quickest way to start is our free Supabase leak check: paste the public address of an app you own, and it reads that page and its scripts the way a visitor's browser would, then tells you in plain English what a stranger could already see. The rest of this post explains what that check looks for, how to repeat each step by hand, and what to do with what you find.

Why "public key in the browser" isn't the leak

Supabase gives every project two kinds of keys. Supabase's docs describe the publishable key (the older version is called the anon key) as safe to put in a web page, because "it only reaches what Row Level Security allows." The secret key (the older version is service_role) bypasses every Row Level Security (RLS) rule and must never reach a browser.

So finding the publishable key in your page isn't a leak. The leak is one of these:

  • A table that answers a stranger with real rows, because RLS is off or a rule says "everyone".
  • A secret key sitting in your JavaScript.
  • A storage bucket set to public that holds files that should be private.
  • Extra clues, like a public source map, that make the first three easier to find.

We covered the theory in is the Supabase anon key safe in the browser?. This post is the hands-on version.

The stranger's-eye test: 5 steps

You only need a laptop, Chrome or Firefox, and the live address of your own app. Only test apps you own.

Step 1: Find your project address and key, like a stranger would

Open your live app in a private window. Press F12 to open developer tools and go to the Network tab. Refresh the page and type supabase.co into the filter box.

You'll see requests to an address like https://abcdefgh.supabase.co/rest/v1/.... Click one and look at the request headers. The apikey header holds the key your app uses. Note two things:

  • If it starts with sb_publishable_, or is a long string starting with eyJ that your dashboard labels anon, that's expected.
  • If it starts with sb_secret_, or matches the service_role key in your dashboard, stop here and go straight to "If you find a secret key" below.

Step 2: Search the scripts for secrets and table names

Still in developer tools, open the Sources tab (Chrome) or Debugger (Firefox). Press Ctrl+Shift+F (Cmd+Option+F on a Mac) to search across all loaded files. Search for each of these:

  • sb_secret_ and service_role
  • sk_live and sk_test (payment keys)
  • .from( — this shows the table names your app reads, such as .from('orders')

Write down every table name you find. A stranger doesn't need a list of your tables from Supabase. Your own JavaScript usually tells them.

While you're here, look at the file list. If you can see your original source files with readable names and comments, your site is publishing source maps. They aren't a leak on their own, but they make your code much easier to read for anyone looking for weak spots.

Step 3: Ask each table for one row, with only the public key

This is the step that matters most. For each table name from Step 2, ask Supabase for a single row using only the public key, with nobody signed in. In a terminal:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/orders?select=*&limit=1" \
  -H "apikey: YOUR_PUBLISHABLE_OR_ANON_KEY"

Read the answer:

  • [] (an empty list): the table is reachable, but your rules returned no rows for a stranger. Usually good.
  • An error such as permission denied for table: the stranger's role has no access to that table at all. Good, if that's intended.
  • Real data: a stranger can read that table. If it's anything other than public content (a menu, blog posts, product listings), that's a leak.

This is exactly what the free leak check automates: with only the public key, it asks each listed table for a single row, counts the tables that answer, and doesn't keep the rows.

Then repeat the test as a signed-in user, since "any logged-in user can read everything" is just as common. Sign in as a test account, copy the Authorization: Bearer ... header from one of your app's requests in the Network tab, and add it to the same command. If test user A can read rows that belong to user B, the rules only check that someone is signed in, not that the row is theirs. Our two-account test covers that check in the app itself.

Step 4: Check your storage buckets

Supabase Storage has two kinds of bucket. Supabase's docs say a public bucket "effectively bypasses access controls" for reading files, so anyone with a file's address can open it. A private bucket checks your rules on every download, or uses signed links that expire.

To check yours:

  • In the Network tab, filter for storage/v1/object. Addresses containing /object/public/ come from a public bucket.
  • In your Supabase dashboard, open Storage and look at which buckets are marked public.
  • For each public bucket, ask: would I be fine if a stranger saw every file in here? Profile pictures and product photos, yes. Invoices, ID documents, contracts or exports, no.

If file names follow a pattern, such as invoices/1001.pdf, invoices/1002.pdf, a stranger who sees one address can guess the rest. Private files belong in a private bucket, served with signed links.

Step 5: Look at the security headers

Finally, look at what your page tells the browser to protect itself. In the Network tab, click the first request (your page itself) and read the response headers. Look for:

  • Content-Security-Policy: limits which scripts the page will run.
  • Strict-Transport-Security: tells browsers to always use HTTPS.
  • X-Frame-Options (or a frame-ancestors rule in the policy): stops other sites from loading your app inside a frame.

Missing headers don't leak data by themselves, but they make other attacks easier. They're usually a quick fix in your hosting settings.

How to score what you found

  • Red, fix today: any secret key in the browser; any table returning private rows to a stranger; private documents in a public bucket.
  • Amber, fix this week: signed-in users can read other users' rows; guessable file names in a public bucket; public source maps on an app holding customer data.
  • Green: tables return [] or permission denied to strangers, only public content answers, and private files sit in private buckets.

If you find a secret key

Treat it as already copied. Supabase's docs say new secret keys are blocked when used from a browser (they return 401), but an attacker can still use a leaked key from other tools. Do this in order: move the code that needs the key into a server function, deploy that change, then create a new secret key in Settings > API Keys and remove the old one. If it's a legacy service_role key, follow Supabase's guide to rotating a leaked key. Then check your data for changes you didn't make.

If a table answers a stranger

Don't fix it by turning things off in a hurry. Turn RLS on for the table, then write a rule that limits each row to its owner, and run Step 3 again. Our 30-minute RLS walkthrough covers how to read and fix rules safely. If you're using an AI builder, describe the rule in plain words: "Only the user who created a row can read or change it. Signed-out visitors can read nothing."

FAQ

Run them only on apps you own or have written permission to test. The steps above read what your own public page exposes; don't point them at anyone else's app.

Does the free leak check store my data?

The leak check reads your public page only. Keys and row data aren't saved, and the result stays in your browser. It doesn't sign in, change data or guess passwords.

My tables return an empty list. Am I safe?

You're safe from strangers on those tables. You still need to test as a signed-in user, because the most common gap is "any logged-in user can read everyone's rows".

Want someone to check it with you?

Start with the free Supabase leak check to see what a stranger sees today. If it finds anything red or amber, or you'd just like a person to walk through your rules, book a free technical assessment and we'll go through it with you in plain English.

Sources

Related posts

Next step

Book a free technical assessment.

Thirty minutes. We look at where you are, whether you are about to build, ready to launch, or already live, and tell you what matters first. You leave with a written next step, whether or not you hire us.

Every price is a custom quote after your assessment. No work starts until you accept the proposal.