UpGuard found 16,326 readable Supabase databases in about 300,000 domains probed, roughly one in 18
UpGuard found 16,326 Supabase databases with readable tables among about 300,000 domains it probed, roughly one in 18. The public key is meant to be visible, so the count measures what policies allowed, and the post counts neither reads nor fixes.
By Parminder Kumar Sharma · · 20 min read

One in 18 probed domains answered with a readable table
UpGuard's post says its researchers gathered about 300,000 unique domains with signs of Supabase use and found 16,326 databases with readable tables. Divide the second figure by the first and you get 5.4 per cent, about one in 18. That ratio is derived here: the 300,000 is UpGuard's approximate figure, and the two numbers count different things, databases and domains.
The other side of that fraction is the number the headlines dropped. About 283,700 of the candidate domains, roughly 19 in 20, are not among the 16,326 (also derived). The post does not say how many of those were protected and how many were simply no longer operational.
That matters because of how the candidates were found. UpGuard fingerprinted Supabase from indicators in public JavaScript and from BuiltWith's technology data, and Supabase's own documentation says the key it tells developers to ship in a web page is meant to be readable by anyone. So a visible key was, by inference, the starting condition for the whole sample. It is not what separated the 5.4 per cent from the 94.6 per cent. What separated them, again by inference, was how each database answered a caller who held only what the page itself ships.
What the fraction does not establish is most of what the coverage implies. The post does not say that any of the 16,326 databases was read by anyone other than UpGuard. It does not say how many owners were told, or whether any fixed the problem. It does not say how many were live products rather than demonstrations or abandoned experiments. And it counts what a table's schema suggests, not what its rows hold: the post says it used schemas "rather than trying to read every row of every table".
The rest of this briefing sets out what was measured, where the post and its own charts disagree, what Supabase has said and changed, and what a team using Supabase should check first. It is written for defenders, and it describes no method for extracting data.
What UpGuard did, and what its post leaves out
UpGuard Research published "Everything Everywhere: Systemic Data Exposure in Supabase Apps" on 24 September 2026 under the name of Greg Pollock (page metadata; last modified 25 September). I read the full post and its six charts. The method, in the post's own terms:
- Discovery. Two data sources: BuiltWith's technology fingerprinting, and a Chrome UX Report dataset on BigQuery that supplied raw JavaScript files which can be searched for Supabase database addresses and API keys. Together they produced about 300,000 unique domains. The stated aim was standalone sites on their own primary domains, rather than apps inside the namespaces of platforms such as Lovable or Replit.
- Probe. Each domain was queried for a "users" table, on the reasoning that many applications have one. There were three possible outcomes: nothing accessible (forbidden by policy, or the database no longer operational); no users table but an accessible table named in a hint; or a page of results.
- Assessment. For the 16,326 databases with readable tables, UpGuard classified table schemas to infer data types. It investigated a handful where metadata suggested meaningful exposure, and notified owners where it judged the exposure significant.
- Classification. It scraped each front page and used an AI model to label the business as B2B, B2C, hybrid or not applicable, and to assign an industry.
UpGuard's method as stated in its post of 24 September 2026, set against what the post does not say. Read in full on 29 September 2026.
| Stated in the post | Not stated |
|---|---|
| About 300,000 unique domains from BuiltWith and Chrome UX Report data | How many domains each source supplied, how many were live, or how many distinct databases they map to |
| Each domain queried for a users table, with three possible outcomes | Which key or role the probe used, or the dates the scan ran |
| 16,326 databases exposing readable tables | Whether a readable but empty table counted, or how test and abandoned projects were treated |
| Data types inferred from schemas; a handful checked for real data | How many were checked, or what was kept afterwards |
| Owners notified where the exposure was judged significant | How many owners, how many replied, how many fixed it |
| Aim: standalone sites operating as real businesses and organisations | Any count showing that they all were |
The choices push the number in different directions, and the post quantifies none of them. Asking for one table shape means a database whose sensitive data sits in a table with another name may be missed, which pushes the count of readable databases down. Asking for a users table pushes the PII share up, and UpGuard concedes as much. The domain list can only contain sites whose JavaScript or BuiltWith record revealed Supabase, which excludes others in unknown proportion. And "databases with readable tables" may include projects nobody uses.
The 16,326 is the count one probe returned. It is not an estimate of exposure across all Supabase projects, and it is not a count of exposed businesses.
What the numbers hold, and where the post disagrees with itself
UpGuard's text says: "Over half of the databases had indicators of some PII." Its chart, titled "Types of data exposed", says something narrower. The figure it gives, 55.8, is the percentage of tables with each field in their schema. The geography chart makes the same choice, headed "all 16,326 exposed tables". The post never says the unit is one table per database. Because the probe asks for one table per database, the two counts may coincide, but that is inference: the post does not state it.
What UpGuard's figures establish and what they do not. Figures from the post's charts, read on 29 September 2026; the right-hand column is my reading.
| UpGuard's figure | What it establishes | What it does not establish |
|---|---|---|
| 16,326 databases with readable tables | One probe got a readable table back from each | That anyone else read it, or that each was a live product |
| Any PII: 55.8 per cent of tables | Column names suggest personal data, most often a name (42.8), email (38.8) or phone (25.8) | That the columns hold real values |
| Password or PIN: 8.2. Hard credential: 9.7 | Schemas with columns of that kind | Plaintext storage, or what UpGuard means by hard credential |
| Payment system: 12.1 | A payment integration is signalled | Card data: UpGuard says the signal is overwhelmingly reference IDs and billing metadata |
| Authentication tokens | The post's prose groups them with passwords as a smaller share | A separate count: no chart row is labelled tokens |
If the chart's percentages applied to all 16,326, which is an assumption given the unit question, they would come to roughly 9,100 with any PII, 1,340 with a password or PIN and 1,580 with a hard credential (derived, not UpGuard's counts). BleepingComputer's headline lists PII, passwords and auth tokens side by side; its body text is closer to the post, which says over half showed PII and a smaller share showed passwords or tokens.
UpGuard also writes that business type made no difference to the kinds of data likely to be exposed. Its own B2B against B2C chart agrees for PII (55.5 per cent of 3,153 B2B tables against 54.9 of 8,926 B2C) and much less for hard credentials (12.9 against 8.3). The industry differences are larger. Ecommerce and retail tables (1,903) carry a name field in 57.0 per cent against 33.1 for education (1,474). Gambling and betting (268 tables) shows a password field in 13.4 per cent and a hard credential field in 14.6, the highest of any industry listed. Groups this small are indications, not rankings.
Five named exposures are a handful, not the rate
UpGuard describes five exposures it investigated after schema screening. They show what a bad one contains. They do not show how typical it is, because they were picked where the metadata suggested meaningful data.
The five exposures UpGuard describes, as stated in its post. Counts are UpGuard's; the note column is derived or mine.
| Exposure, as UpGuard describes it | What UpGuard counted | Note |
|---|---|---|
| US valet service, used as a CRM back end | Over 100,000 customers; about 43,000 with email and full name; about 78,000 with licence plates; 665 staff records | 4,560 corporate-domain emails is 10.6 per cent of 43,000; UpGuard says about 11 per cent, so consistent |
| India adult creator platform | 65,467 people in the users table, with identity document fields and payout accounts; over 100,000 private messages | The post gives no further counts |
| Philippines OTP service | Over 2,000 users; over 100,000 SMS messages, 95 per cent of them OTP codes | 2,000 to 2,400 sampled texts were person to person, mostly ridesharing, from people who appear unrelated to the SIM farm |
| African government consulate | 25,000 users with PII and physical addresses; a field naming emergency housing | A vulnerable population, in UpGuard's words |
| Canadian immigration and relocation service | Nearly 5,000 user records; 884 with a plain text password | 884 of 5,000 is 17.7 per cent, so at least that share (derived) |
Two of these matter to a defender beyond the headline. The Philippines database shows collateral exposure: it held messages from people who had no relationship with the operator. The valet database shows a supply chain angle: UpGuard found about 4,560 email addresses on third-party corporate domains, including regional employers such as universities and Fortune 500 companies. Neither group chose that service.
The key is meant to be public
Supabase's API key documentation is plain about this. For anything you ship, a browser, mobile app, CLI or script, it says to use the publishable key because "Anyone can read it, so it only reaches what Row Level Security allows". A secret key is for code you control, because it bypasses Row Level Security. A request with no signed-in user runs as the Postgres role anon; a signed-in user runs as authenticated; a secret key runs as service_role, which has the BYPASSRLS attribute. Supabase says it is deprecating the legacy anon and service_role keys by the end of 2026 in favour of publishable and secret keys.
One control is weaker than it sounds. The documentation says a secret key does not work in a browser, because Supabase matches on the User-Agent header and returns HTTP 401, and then adds that an attacker can still use a leaked key from other tools. A browser block is a convenience. It is not a control.
Change one setting at a time below. The same public key returns every row, none, or only the caller's own, depending on the two layers behind it.
So "the anon key is exposed" is not a finding. It is how the product is designed to work. The finding is what the role behind that key was allowed to do: which tables it could reach, which is the grants layer, and which rows in them, which is Row Level Security. UpGuard's post names three causes: missing RLS, policies that exist but do not restrict enough, and public keys used as if they were secret. It counts none of them, and it never mentions grants.
"Secure by default" and "RLS is on", against the record
Supabase's Chief Information Security Officer, Bil Harmer, told TechCrunch that the company had not seen the research, that its projects are "secure by default", and that security is shared: "We provide secure defaults and tooling, and customers control how their own projects are configured." Parts of the record support each half. Parts of it do not.
Supabase's position as TechCrunch reported it on 25 September 2026, against Supabase's own documentation, changelog and blog. Read on 29 September 2026.
| Position | On the record | Not on the record |
|---|---|---|
| Projects are "secure by default" | Tables made in the Dashboard have RLS on by default. New projects began to stop exposing new tables to the Data API by default from 30 May 2026, in a rollout over a few weeks | That tables made in the SQL editor or by tools have RLS on. On existing projects, new public tables kept default grants for anon until the change is enforced on 30 October 2026 |
| Customers control their own configuration | Supabase's January 2026 retro: application-level security remains the responsibility of each customer | How many of the 16,326 owners received the advisor emails and alerts Supabase says it sends, or acted on them |
| The company had not seen the research | Stated to TechCrunch on 25 September | Whether UpGuard told Supabase before publishing, or whether Supabase has since contacted affected owners |
It would be unfair to stop there, because Supabase has changed a great deal, on dates it publishes:
- 7 January 2026. A security retro lists 2025 changes: RLS on by default for Dashboard tables, warnings and email alerts for tables without RLS, an open-source security linter behind the Security Advisor, weekly summary emails to organisation owners, an option to disable the Data API, and new API keys.
- 17 February 2026. A notice that anon-key access to the Data API's OpenAPI schema would end, for new projects from 11 March and all projects from 8 April. Supabase says the endpoint listed tables, columns and types but did not expose row data.
- 28 April 2026. An opt-in setting so new tables are not exposed to the Data API automatically. It became the default for new projects from 30 May and is enforced on all existing projects on 30 October 2026, 31 days from this briefing (derived).
Read the limit of that last change as Supabase writes it: "Existing tables are not affected in your project, they keep their current grants and stay reachable." The rollout protects tables created after it. It does not close a table that is open today. Supabase's own reason for the change is the mechanism UpGuard alleges: agents, CLI scripts and AI platforms now create tables too, and, in Supabase's words, "many of those operations do not have a human reviewing the diff".
"RLS is on" is the second label to distrust. Supabase's documentation says a policy that reads to anon using true gives every signed-out visitor read access to every row the role's grants reach. That is a policy, and it is open. Views bypass RLS by default because they are usually created with the postgres user. A policy that relies on the user_metadata claim is risky because signed-in users can change that data. Supabase's advisors include checks named permissive RLS policy, RLS references user metadata, security definer view and sensitive columns exposed, which implies that Supabase itself expects a table to have RLS on, pass a glance, and still answer a signed-out request.
The responsibility argument is older than this research. The NVD record for CVE-2025-48757, which UpGuard cites for Matt Turner's 2025 report on databases generated by Lovable, is marked deferred and carries the supplier's dispute note: each customer accepts responsibility for protecting their application's data. Where responsibility lies is a real dispute. It does not change whose data is in the table.
Who measured it, and what they sell
UpGuard sells third-party risk, attack surface management and breach risk products, and offers free scanning tools; a demo request sits beneath the post. A study that finds thousands of exposures by scanning the internet for a technology fingerprint is also a demonstration of that product category. That is not a reason to doubt the counts. The method is described in enough detail to judge, and in the coverage I read Supabase has not disputed any figure. It is a reason to read the causal claims separately from the counts.
The causal claims are the AI ones. The post says: "The common thread is that these sites are created by AI coding agents and the humans are unaware of the configuration." Its method fingerprints Supabase and classifies the business; it does not measure how any site was built. BleepingComputer reports that UpGuard stressed its scans do not establish that every affected site used an AI coding agent. I could not find that sentence in the post, so I attribute it to the article.
Supabase's own June 2026 funding post says more than 60 per cent of new databases are launched by some sort of AI tool, and credits Claude Code and Codex for accelerating growth. Together with Supabase's April notice, that makes the AI-agent explanation plausible. It does not turn it into a measurement.
Two smaller checks on how the figures travel. TechCrunch says the majority of the exposed datasets appear to be in the United States. UpGuard's own chart shows 976 exposed tables for the US, 6.0 per cent of 16,326, with 47.7 per cent of tables in an unknown region, so I have not repeated the claim. And UpGuard's summary of an earlier study, Modern Pentest's scan of Y Combinator startups, gives 28 per cent of 107 startups exposing PII. The study itself audited 71 of the 107 and found 20 leaking PII: 28.2 per cent of 71, 18.7 per cent of 107. The denominator matters in the prior study as it does in this one.
What this means for a UK team
UpGuard's geography chart lists Great Britain with 354 exposed tables, 2.2 per cent of 16,326 (derived), behind the United States (976), India (960) and Brazil (746) and ahead of France (274) and Germany (231). It assigns a region to only 52.3 per cent of tables (derived from the 47.7 per cent marked unknown), and the post does not say how operator country was decided. So 354 is a count of what UpGuard could place, not a UK total.
UpGuard suggests Europe's data protection laws tend to drive better practice. Its chart shows a password field in 6.3 per cent of Europe's 2,260 tables against 13.7 per cent of East and South East Asia's 886. That is consistent with the suggestion. It does not test it.
If you run a Supabase project that holds personal data about people in the UK, the legal question is separate from the technical one, and it belongs to your data protection officer rather than to this briefing. The ICO's guide defines a personal data breach as a breach of security leading to, among other things, unauthorised disclosure of, or access to, personal data. It expects a notifiable breach to reach the ICO within 72 hours of becoming aware of it, where feasible, and expects you to record breaches whether or not you notify. Whether a table that answered signed-out requests, with no evidence that anyone read it, counts is an assessment for you to make and document.
Audit checklist, in the order worth doing
If you have an hour, do the first three items and the catalogue checks below: inventory, advisor, and a list of every table the signed-out role can touch. Everything after that narrows the risk further. Nothing here needs you to read a single row of customer data.
Take this with you
Supabase audit, first hour to first week
- List every Supabase project the organisation owns, including personal, hackathon, contractor-built and abandoned ones, and mark which use the Data API. Where an app only connects directly to Postgres, turn the Data API off.
- Run the Security Advisor on each project (in Studio, with the supabase db advisors command, or through the MCP get_advisors call) and clear errors and warnings first: RLS disabled in public, policy exists with RLS disabled, permissive RLS policy, sensitive columns exposed, RLS references user metadata, security definer views and functions, public bucket allows listing.
- For every table and view in an exposed schema, confirm RLS is on and that anon holds no grant unless signed-out access is intended. Write one policy per operation, name the role each applies to, and give any policy that says true to anon a named owner and a written reason.
- Make views obey the caller's policies (security_invoker on Postgres 15 and above) and review every security definer function: RLS does not apply to functions, and grants decide who may call them.
- Check that the key in every browser bundle, mobile build, public repository and CI log is the publishable key or the legacy anon key, never a secret key or a service_role key. If a secret key ever shipped, fix the cause first, then create a new key, replace it everywhere, confirm, and retire the old one, as Supabase's documentation sets out.
- Storage: keep buckets private unless the files are meant to be public, put policies on storage.objects, and clear the advisor finding for public buckets that allow listing.
- Do not keep passwords, one-time codes, session tokens or third-party API keys in tables the Data API can reach. Let Supabase Auth hold credentials, keep other secrets server-side, and use column-level privileges for sensitive personal data.
- Read your API Gateway and PostgREST logs for signed-out reads of sensitive tables inside your retention window, which depends on your plan. If a table was open and you cannot show it was not read, take it to your data protection officer.
- Stop it recurring: put grants, RLS and policies in the same migration, add an event trigger that enables RLS on every new table, write tests that assert the signed-out role is denied (supabase test db), and review AI-generated migrations for all three before they merge.
- Diary 30 October 2026: on existing projects new tables will need explicit grants, but existing tables keep theirs, so revoke what should not be reachable now. Plan the move from the legacy anon and service_role keys to publishable and secret keys, which Supabase says it is deprecating by the end of 2026.
-- 1. Tables in the public schema with RLS switched off
select schemaname, tablename
from pg_tables
where schemaname = 'public' and not rowsecurity
order by tablename;
-- 2. What the signed-out role (anon) is granted on public tables and views
select table_name, string_agg(privilege_type, ', ' order by privilege_type) as anon_privileges
from information_schema.role_table_grants
where grantee = 'anon' and table_schema = 'public'
group by table_name
order by table_name;
-- 3. Policies whose condition is simply true
select tablename, policyname, cmd, roles, qual, with_check
from pg_policies
where schemaname = 'public' and (qual = 'true' or with_check = 'true')
order by tablename, policyname;
The checks cannot tell you whether a policy matches intent. That needs someone who knows what each table is for, and a test that asserts a signed-out request gets nothing. For the wider pattern of secrets ending up in AI-assisted code, see our briefing on AI commits and secret leaks.
The question that matters
Public keys are supposed to be visible. Policies are supposed to be the lock. UpGuard's figures suggest that for about one in 18 of the domains it probed, the lock was either never fitted or fitted open, and the post cannot tell you which, how long ago, or whether anyone walked in.
So ask it of your own projects: if a signed-out visitor asked your database for its users table right now, which of your tables would answer, and who decided that they should?
Key facts
Sources
- PrimaryEverything Everywhere: Systemic Data Exposure in Supabase Apps, published 24 September 2026 (page metadata), the research itself, read in full with its six charts for the method, the 16,326 count, the schema indicators, the five named exposures and the geographyUpGuard Researchaccessed 2026-09-29
- PrimaryAPI keys documentation, used for the publishable key, secret key, role mapping, the browser block on secret keys and the deprecation of anon and service_role by the end of 2026Supabaseaccessed 2026-09-29
- PrimaryRow Level Security documentation, used for grants against policies, the effect of no RLS, policies that say true to anon, views, user_metadata and the audit stepsSupabaseaccessed 2026-09-29
- PrimarySecuring your API documentation, used for default grants on existing projects, RLS defaults by table creation route and revoking default privilegesSupabaseaccessed 2026-09-29
- PrimaryBreaking Change: Tables not exposed to Data and GraphQL API automatically, posted 28 April 2026, used for the rollout dates (30 May and 30 October 2026), the statement that existing tables keep their grants and Supabase's stated reasonSupabaseaccessed 2026-09-29
- PrimaryBreaking Change: Removing access to OpenAPI spec via the anon key, 17 February 2026, used for the rollout dates and the statement that the endpoint did not expose row dataSupabaseaccessed 2026-09-29
- PrimarySupabase Security Retro: 2025, 7 January 2026, used for the platform changes listed, the alerts and weekly emails, and the shared responsibility statementSupabaseaccessed 2026-09-29
- PrimarySupabase Series F, June 2026, used for the statement that more than 60 per cent of new databases are launched by some sort of AI tool and the credit to Claude Code and CodexSupabaseaccessed 2026-09-29
- PrimaryAdvisors documentation, used for the list of security check names and the Studio, CLI and MCP routes to run themSupabaseaccessed 2026-09-29
- PrimaryStorage Access Control documentation, used for bucket policies on storage.objects and the warning about listingSupabaseaccessed 2026-09-29
- PrimaryLogs documentation, used for the log types available (API Gateway, PostgREST, Storage) and the statement that retention depends on the planSupabaseaccessed 2026-09-29
- PrimaryCVE-2025-48757 record, used for the Lovable description, its deferred status and the supplier's dispute noteNIST National Vulnerability Databaseaccessed 2026-09-29
- Primary20 Million Rows Exposed: A Supabase Security Study of YC Startups, 26 January 2026, used to check the denominators of the study UpGuard summarises (107 identified, 71 audited, 20 leaking PII)Modern Pentestaccessed 2026-09-29
- PrimaryPersonal data breaches: a guide, used for the definition of a personal data breach, the 72 hour notification duty and the duty to record breachesInformation Commissioner's Officeaccessed 2026-09-29
- Reported bySome Supabase customers are publicly exposing reams of people's data to the web, 25 September 2026, used for Supabase's statement (its CISO, Bil Harmer) and the claim about US location that the article does not repeatTechCrunchaccessed 2026-09-29
- Reported byOver 16,000 Supabase databases expose PII, passwords, auth tokens, 28 September 2026, the news pointer; used for the coverage's framing and its report that UpGuard stressed the scans do not establish AI authorshipBleepingComputeraccessed 2026-09-29


