“Is Supabase safe?” sounds like it wants a single word back, and the honest answer splits into two halves that pull in opposite directions. Supabase, the company and the infrastructure, holds SOC 2 Type 2 certification, the paperwork an enterprise buyer checks before signing anything. The apps built on top of it are a separate question, and the audits answer it less kindly. Across 14 of the 21 third-party apps in the AxonBuild corpus where authorization applied at all, audited in June and July 2026, the Authorization pillar averaged 42.1 out of 100, the fifth-worst score of the twelve categories I check, one pillar inside a larger 26-app audit I’ll lay out in full elsewhere. The platform passed its exam. Most of what gets built on it hasn’t.
That’s the verdict, stated once so nothing below needs to hedge around it. The rest of this post takes it apart: which half of “is Supabase safe” the platform actually answers, which half is yours alone, and the specific shapes a Supabase app takes when it looks locked down and isn’t.
Is Supabase safe? Yes as a platform, usually not as you’ve configured it
Supabase publishes its compliance status directly, and it isn’t marketing gloss: “Supabase is SOC 2 Type 2 compliant.” That is a real certification with a real audit behind it, the same kind a fintech due-diligence team asks for before approving a vendor. HIPAA is a narrower story with plan tiers and an add-on price behind it, covered separately in is Supabase HIPAA compliant and on which plan. On the infrastructure question, the honest answer really is yes.
The apps I audit sit on top of that infrastructure, and the certification doesn’t travel with them. A SOC 2 report covers Supabase’s own operational controls: how it manages its data centers, its access controls, its incident response. It says nothing about whether the app you built enforces row-level security (RLS) correctly on the orders table someone added last month, or whether a policy checks the caller’s identity or just whether they’re logged in. That gap is exactly where the number above comes from.
The two questions hiding in one search
“Is Supabase safe,” or how secure is Supabase, reads as one question and is actually two, which is why the search results for it split the way they do. One half asks whether the company can be trusted with infrastructure: uptime, encryption at rest, who can physically reach the servers, the same platform-or-your-code split that decides whether Supabase is slow or your queries are. The other asks whether your specific app, with your schema and your policies, keeps one customer’s data away from another. A long-running r/Supabase thread argues the second question and calls it the first: the observation that “the anon key can operate the database directly” is technically true and says nothing about whether you built the layer meant to stop it. Supabase’s trust page answers only the first question, correctly, and never claims to answer the second, because it can’t. Nobody outside your project can.
Supabase being secure and your Supabase app being secure are two different claims, and only the second one is your job.
What Supabase secures for you, and where the line sits
Under the hood, a Supabase project is Postgres with auth, storage, and a generated API already wired to it, which is most of why it became the default. Why everyone uses Supabase and what the default costs later prices those conveniences against what the audits found. Every request that reaches your database’s API arrives carrying one of two roles. Supabase’s documentation is direct about it: anon for a visitor who hasn’t logged in, authenticated for one who has, and what either role can actually touch depends entirely on the policies you write for it. Supabase built the door and correctly tags everyone who walks up to it as anon or authenticated. Whether the door has a working lock is a decision made in your schema, one table at a time. The same line runs through the rest of the platform: what a Supabase backup actually covers on free versus Pro is fixed by the plan you’re on, and whether that backup has ever produced a working restore is yours.
That arrangement is why the public anon key sitting in your browser’s network tab isn’t, by itself, a problem. The key was built to be public, the same way a building’s street address is meant to be found. The reassurance stops being true the moment a table that key can reach has row-level security switched off, or a policy that doesn’t check who’s asking, and the failure is silent: the app behaves exactly the same, right up until someone who shouldn’t see a row asks for it.
A correct policy is short, and the shape matters more than the syntax:
create policy "read own rows"
on public.orders
for select
using (auth.uid() = user_id);
That single using clause is the entire difference between a policy that exists and a policy that works. A policy reading using (true) passes every check that only asks whether row-level security is enabled, and hands back every row in the table to anyone who requests it.
What the audits found: authorization is where it breaks
Ranked against the eleven other categories I score across the corpus, Authorization sits fifth from the bottom at 42.1, computed only across the 14 apps where the pillar applied at all. Reliability and dependency hygiene score worse still, but authorization is where a low score turns into one specific person reading one specific stranger’s data, which is closer to what a search for “is Supabase safe” is actually asking. Underneath that average sit two counted findings from the same 21 third-party apps: 9 carried a genuine row-level security hole, and 7 let a signed-in user reach rows that belonged to a different customer.
I read a Supabase-backed app once where the profiles table’s own policy was written exactly right: a signed-in user could select their own row, nothing else. Then I found a second policy on the same table that read using (true). Postgres doesn’t ask which policy you meant. When more than one permissive policy applies to the same role, access is granted if any of them says yes, so the correct policy became decorative the moment the second one existed. One query, using nothing but the public key, returned every user’s email, phone number, and exact coordinates. Nobody had disabled row-level security. Nobody had written a bad policy on purpose. Two policies, each defensible on its own, combined into a leak neither author would have shipped alone.
A sharper version of the same failure doesn’t even need a second policy. A Postgres function declared security definer runs as the table’s owner and steps outside row-level security entirely, by design, for the cases where that’s exactly what you want. In the worst version I’ve found in this corpus, a function like that trusted whatever id the caller handed it instead of checking who was actually asking, walking straight past table policies that were otherwise correct. Nothing about the function’s syntax is wrong: a linter can flag that a security definer function is exposed, not that its body never checks who’s calling. A Beyond the Demo Audit checks every table’s policy against the queries your app actually runs, which is what surfaces that class of gap before a stranger finds it first.
The three ways a “safe” Supabase app is actually open
A Supabase app that looks locked down is usually open in one of three shapes: a table with row-level security never enabled, a policy that checks only that the caller is logged in, or a service_role key shipped in client code. The fix for each is different enough that “turn on RLS” doesn’t cover all three.
No RLS on a table your app actually reaches is the first shape, and it’s easy to miss because the default depends on where the table was born: Supabase enables RLS by default only on tables created with the dashboard’s Table Editor, and a table created in raw SQL or a migration, which is how AI builders create nearly all of theirs, starts with RLS off until someone flips the switch. The second is a policy that checks “logged in” and stops there, exactly what a second permissive policy and a security definer function both produce from different directions. The third doesn’t touch RLS at all: a service_role key, built specifically to skip every policy you’ve written, sitting in code that ships to a browser. Each shape hands out the whole table, or the whole database, to whoever finds it.
Closing the first two, the practices that actually hold row-level security together once you’ve turned it on, is its own subject. Turning it on isn’t the finish line either, and that’s worth a separate explanation. And if the answer you landed on is to leave, the six real Supabase alternatives and what you own after the switch change which four things you configure, not whether you configure them.
How to check your own project
You don’t need a full technical drill to get a first, honest read on your own project. Create a second account in your app, log in as it, and try to read or write the first account’s data through your app’s normal interface, changing only an id, not a raw request against the database. If it works, that’s the leak, whether the underlying cause is a missing policy, an always-true one, or a second policy quietly undoing a correct one. The exact anon-key request that turns this into a two-minute test, plus the order to fix things in once you find something, is its own explanation; this version just tells you what you’re looking for before you go looking for it. And if you already flipped row-level security on and got an error back instead of a leak, that’s a friendlier problem with a different fix.
Authorization is one piece of whether your app is actually ready to launch, and the same trust-boundary gap explains why AI coding tools ship security holes by default on every stack, not just Supabase’s. This post covers the read side of that gap. The write and delete side of the same story is just as real and just as quiet, and when a delete does reach live data, the first hour after someone accidentally deletes the production database decides how much of it comes back.
The rest of the Supabase series
Security is one question a Supabase project raises and not the only one. The rest have their own pages: what a Supabase backup covers per plan tier, why a Supabase app gets slow and what to check first, recovering a production database someone deleted, what one database with no staging environment costs, the Supabase HIPAA answer, plan by plan, what the Supabase alternatives actually change, and why Supabase became the default for AI-built apps.
Common questions about Supabase security
Is Supabase secure?
As a platform, yes. How Supabase handles security at the infrastructure level is covered by a real SOC 2 Type 2 audit, not a marketing claim. Whether a specific Supabase app is secure depends on the row-level security policies its builder wrote, and that part was never Supabase’s to certify. The same platform-versus-your-app split decides whether a Base44 app is compliant, on a platform whose certificates are just as real.
Is the Supabase anon key safe to expose?
Yes, by design. Supabase’s own documentation maps every request to the anon or authenticated role, and what either role can reach depends entirely on the policies attached to your tables. The key being public is fine. A table it can reach with no working policy behind it is not.
Is row-level security enabled by default in Supabase?
Only for tables created with the Table Editor in the dashboard; Supabase’s own guide states RLS “is enabled by default on tables created with the Table Editor.” A table created in raw SQL or through a migration ships with RLS off until someone enables it, and migrations are how AI builders create tables, which is exactly the gap the number in this post measures.
Is Supabase safe for production?
The platform has been production-grade for a while, and its SOC 2 status says so in writing. Whether a given app is ready for production is a narrower, harder question, and the honest way to answer it is to check the policies against the queries your app actually runs, not to ask whether the platform underneath them can be trusted.
Don't guess where you stand.
The free 2-minute Beyond the Demo Scorecard estimates where you stand across 12 readiness areas and shows what to check first.