16,326 Supabase Databases Were Readable: Run UpGuard's Test on Yours (2026)

16,326. That's how many Supabase databases UpGuard found with publicly readable tables, out of roughly 300,000 domains that showed signs of using Supabase. TechCrunch first reported the research on 2026-09-25 (as "around 16,000"), and UpGuard's own write-up gives the exact figures. Over half of the exposed databases had indicators of personal data. A smaller share had passwords or auth tokens. A very small number had what looked like credit card data.

The method is the part worth paying attention to. UpGuard didn't break anything. It read each site's public JavaScript, found the Supabase key that ships there, and asked the database for a table called users. Anyone can do that to your app, so you should do it first.

TL;DR

UpGuard probed ~300,000 Supabase-backed sites with the public key from their own JavaScript and found 16,326 databases with readable tables. It wasn't a Supabase breach: these were customer tables without working Row Level Security. Tables created through the API, which is how coding agents work, don't get RLS by default. Below is the same two-minute test, run against your own project, plus the SQL that finds every unprotected table.

What UpGuard actually did

From UpGuard's write-up (the page is dated 2026-10-14, later than the September press coverage):

  1. Find Supabase sites. UpGuard used BuiltWith for Supabase usage indicators and the Chrome UX Report dataset for raw JavaScript files. Supabase sites "can be fingerprinted by looking for Supabase key names" in public JS.
  2. Ask for a users table. Each domain got the same query. The answer fell into one of three buckets: nothing accessible, no users table but an accessible table name offered as a hint, or a page of real rows.
  3. Count. 16,326 databases exposed readable tables.

That middle bucket is worth understanding. When you ask PostgREST (the API layer under Supabase) for a table that doesn't exist, it can reply with a suggestion along the lines of "Perhaps you meant the table public.profiles." So a guess at users doesn't have to be right to be useful. It tells the person asking what is there.

Supabase's response. Supabase CISO Bil Harmer told TechCrunch the company hadn't seen the research, that projects are "secure by default," and that security is a shared responsibility. UpGuard's report doesn't say any of the data was accessed maliciously. It says UpGuard notified owners where the exposure was significant.

Why AI-built apps land in that 16,326

UpGuard doesn't claim every exposed site was built with an AI agent. But it names the mechanism that makes agent-built projects likely candidates: tables "created programmatically through the API, which is how coding agents interact with Supabase," don't get RLS enabled by default.

Supabase did change the Table Editor so new tables there get RLS turned on. An agent doesn't use the Table Editor. It runs CREATE TABLE through SQL or a migration, and that table starts life with RLS off unless the agent also writes ALTER TABLE ... ENABLE ROW LEVEL SECURITY and at least one policy.

Whether the public key can read that table straight away depends on your project's age. Projects created before May 30, 2026 grant the public anon role access to new public tables automatically, so an agent-made table there is readable the moment it exists. Newer projects don't hand out that grant, and existing projects stop doing it for new tables on October 30 (more on that below). Lovable says it enables RLS by default and runs a linter for missing RLS. With Bolt, Cursor or Claude Code writing raw SQL migrations in an older project, you're relying on the agent to remember both steps, every time, for every table.

UpGuard groups what it found into three causes:

  • No control at all. RLS off, so the public key reads everything.
  • Policies that don't restrict enough. RLS on, but a policy like using (true) lets everyone in anyway.
  • Public keys treated as secret. The anon or publishable key is meant to be in the browser. If your app's safety depends on nobody finding it, it has no safety.

Run the test on your own app

Only do this against a project you own.

1

Find your project URL and public key the way UpGuard did. Open your live site, open DevTools, go to the Sources (or Network) tab and search the JavaScript for supabase.co (or your custom API domain, if you set one up). Next to the URL you'll find a long eyJ... string (a legacy anon key) or an sb_publishable_... key. That's what every visitor can see.

2

Ask for a users table, exactly as UpGuard did.

Terminal
URL="https://YOUR-PROJECT-REF.supabase.co"
KEY="paste-the-public-key-from-step-1"

curl -s "$URL/rest/v1/users?select=*&limit=1" -H "apikey: $KEY"

How to read the answer:

ResponseWhat it means
A JSON row with real dataThat table is readable by anyone. Fix it now.
[]Either RLS is blocking you or the table is empty. Test a table you know has rows.
An error with a "Perhaps you meant..." hintNo users table, but the hint just told you another table name. Try that one.
permission deniedThe public role has no grant on that table. Good.
3

Repeat for the tables your app actually has. profiles, customers, orders, messages, waitlist, subscriptions are the usual suspects. One readable table is enough to be in UpGuard's count.

Find every unprotected table at once

The curl test shows you what an outsider sees, one table at a time. To see everything, run this in the Supabase SQL Editor:

SQL Editor
-- Tables with Row Level Security turned off
select tablename
from pg_tables
where schemaname = 'public' and rowsecurity = false;

-- Can the public anon role read each table at all?
select tablename,
       has_table_privilege('anon', format('public.%I', tablename), 'select') as anon_can_select
from pg_tables
where schemaname = 'public'
order by tablename;

-- Views in public: these skip RLS unless created with security_invoker
select table_name
from information_schema.views
where table_schema = 'public';

A table that shows up in the first query with anon_can_select = true in the second is readable by anyone holding your public key. If it holds user data, it needs RLS enabled and real policies. Views are easy to miss: by default a Postgres view runs with its owner's rights, so it can serve rows that the table's RLS would block. Recreate any view over user data with (security_invoker = true). Our step-by-step RLS guide walks through both. Then read every existing policy for using (true), which turns RLS on and lets everyone through anyway.

Supabase's grants change on October 30, 2026 won't save an exposed table. From that date, new tables in existing projects stop getting automatic grants, but tables you already have keep theirs. Read what that change does and doesn't do, and don't "fix" a broken table with GRANT ALL ... TO anon.

What CheckYourVibe catches, and what it doesn't

Honest version: our scanner detects that your site uses Supabase and flags the worst key leak, a service_role key in your frontend bundle, which bypasses RLS entirely. It doesn't query your tables, because poking at someone's database is a line we don't cross from the outside. That's why the test above is here: it's the one check only you should run.

If you've just turned on RLS for a table an agent created, run a scan afterwards too. The same project that skipped RLS often has other things in the bundle it shouldn't.

Was Supabase hacked?

No. UpGuard's research describes misconfigured customer projects, not a breach of Supabase. The exposed tables were readable through the normal Supabase Data API using the public key each site already ships in its JavaScript. Supabase's CISO told TechCrunch the company hadn't seen the research and that projects are secure by default.

Was the exposed data actually stolen?

UpGuard's report doesn't say any data was accessed maliciously. It says UpGuard notified application owners where the exposure was significant. Readable is still bad: anyone who runs the same query gets the same answer.

Why are AI-built apps more likely to have this problem?

UpGuard points out that tables created programmatically through the API, which is how coding agents interact with Supabase, don't have Row Level Security enabled by default. Supabase turned RLS on by default for tables created in the Table Editor, but a table an agent creates with SQL doesn't get that safety net.

Is the anon key supposed to be secret?

No. The anon or publishable key is designed to be public and is in every Supabase frontend. That's exactly why it can't be your security: Row Level Security policies decide what that key can read. UpGuard lists treating public keys as if they were secret as one of the causes it saw.

Does Supabase's October 30 grants change fix this?

No. From October 30, 2026, new tables in existing projects stop getting automatic grants to anon and authenticated. Tables you already have keep their current grants, so anything exposed today stays exposed until you fix its RLS.

See what your frontend gives away

CheckYourVibe scans your live app for leaked Supabase keys, exposed configs and missing headers, and explains each one in plain English.

Security Stories

16,326 Supabase Databases Were Readable: Run UpGuard's Test on Yours (2026)