On September 25, TechCrunch reported on research from the security firm UpGuard: out of roughly 300,000 Supabase project domains they scanned, 16,326 had at least one table that anyone on the open internet could read. More than half of those held some kind of personal data: names, addresses, phone numbers. A smaller slice exposed password fields directly, or authentication tokens that would let an attacker log in as someone else without ever guessing a password. The examples UpGuard published were mundane in a way that makes the story worse, not better. Thousands of license plates and vehicle details from a US valet parking app. Contact records from an immigration services firm. Data tied to a foreign consulate. None of it needed a clever exploit. It was sitting behind a URL anyone could construct, because nothing was told to stop them.
The one setting that decided all of it
The cause is not a bug in Supabase. It is Row Level Security, RLS for short, and a quirk in how it gets switched on. Create a table from the Supabase dashboard and RLS is enabled by default, with no policy attached, which means nobody can read a row until you write one. Create the exact same table with a SQL migration, the Supabase CLI, or any tool that talks to Postgres directly, and RLS stays off unless you turn it on yourself. That is a sensible default for someone clicking through the dashboard and a dangerous one everywhere else, because almost no real project gets built by clicking through the dashboard. Projects get built with migrations, with ORMs, and increasingly with an agent writing the SQL on your behalf.
I build headless projects on Supabase for the reasons most developers land there: hosted Postgres you can actually query, a generous free tier, and none of the vendor lock-in that comes with a proprietary CMS. The part nobody spells out clearly enough is that Postgres has no opinion on who can read what until you give it one, and a tool generating your schema for you will happily generate a table with no access control at all if nothing in its instructions tells it to check.
Coding agents did not invent this, they made it faster
UpGuard's own researchers were careful not to pin this entirely on "vibe coding." Blaming Lovable or Replit users alone misses the point. The same pattern shows up in projects built with Claude Code, with Codex, and in plenty of hand-written code from developers who have been doing this for a decade. What changed is the workflow. An agent that can provision a table, wire up an API route, and build the frontend that calls it, all in one sitting, removes the moments where a developer used to stop and actually look at what they had just created. Nobody sits down and deliberately decides to skip Row Level Security. It gets skipped because the pause that used to force the question is gone, and the app runs perfectly well without it right up until someone finds the table.
This is not a new shape of problem, even if the tooling is new. Magento shipped for years with an admin setup that was permissive until a developer locked it down by hand, and stores are still getting backdoored today by people who never got round to it, most recently through the CVE-2026-75650 zero-day that had Adobe pushing emergency hotfixes and store owners rotating every credential on the box. Different stack, same root cause: a secure default that only holds if somebody actually checks it, on a system built by a team who assumed somebody would.
What to actually check this week
If you have a Supabase project running anywhere, whether you wrote every line yourself, inherited it from someone else, or had most of it generated for you, this is worth an afternoon.
- List every table in your public schema and confirm RLS is switched on for each one, not only the ones you remember creating by hand in the dashboard.
- For every table where RLS is on, read the actual policy. Enabled is not the same as correct. A policy that lets any authenticated user select everything is barely better than having none.
- Test access as an anonymous visitor and as a logged-in user who should not have access to a given row, not only as yourself signed in with full admin rights.
- Check your views separately. A view created with security definer can quietly bypass the RLS sitting on the tables underneath it, and it is easy to forget views count too.
- Search your client-side code and your git history for a service role key. It should never leave the server. If you find one in a frontend bundle, rotate it today, not this week.
None of this takes long for a project of normal size. The expensive part was never the fix. It is finding out you needed one after the data has already been sitting in a search engine's cache, or in a scraper's dataset, for six months. If you inherited a Supabase project you did not build from scratch, or if an agent scaffolded the first version of it for you, this is worth doing even if nothing looks obviously wrong today.
Supabase's own CISO told TechCrunch that security is "a shared responsibility," which is true and also exactly what you would expect a vendor to say after a story like this. The defaults on offer are reasonable, and better than most people give them credit for. What gets built on top of them is still yours to get right, whether you typed it or an agent did.
This is not the first Supabase story I have flagged in a weekly round-up this month, and it will not be the last. I have also written about building with AI without it turning into vibe coding, and this is exactly the failure mode that piece was warning about. If you want a second pair of eyes on a Supabase project, whatever built it, get in touch and I will go through the schema with you properly rather than skim it for ten minutes and tell you it looks fine.
Supabase security, answered
Common questions about the RLS exposure and whether your own project is affected.
Possibly, if your tables were created with migrations, the CLI, or an agent rather than clicked together in the dashboard. Those routes leave Row Level Security off unless you turn it on. The quickest check is to open each table and confirm RLS is enabled with a policy attached. If you are not sure, it is worth having someone look.
Row Level Security is the Postgres feature that decides who can read each row. With it off, anyone who can reach your project URL can read the table. The dashboard turns it on by default, but migrations and the CLI leave it off, which is how more than sixteen thousand projects ended up readable by the public.
No. The cause was not a bug in Supabase, it was RLS being left off on tables made outside the dashboard. Built properly, with RLS on and a policy for every table, Supabase is a solid foundation and I use it for exactly that. The danger is treating the default as handled when it is not.
That is the riskiest case, because an agent writing SQL creates tables the same way a migration does, with RLS off unless it is told otherwise. If nobody reviewed the security side afterwards, assume it needs checking. An agent is good at writing the table and less reliable at remembering to lock it down.
Enable RLS on every table that holds real data, then write a policy for each one saying who can read and write it. Never leave a table with RLS on but no policy and assume that is fine, and never ship a public key against tables you have not checked. If that sounds like work you would rather hand over, I can audit it.