A lot of apps built with AI tools store their data in Supabase. It's a good choice, and it lets your app talk to the database straight from the browser, which is a big part of why these tools can build things so quickly. It also means one setting decides whether your users' data is private or open to anyone who knows where to look.
Why this matters
Your app needs a key to talk to Supabase, and that key sits in code that's sent to every visitor's browser. Anyone can find it. That's by design: the public key (usually called the "anon" key) is meant to be visible. What keeps the data safe is a separate layer of rules on the database itself, called Row Level Security.
If those rules are missing, anyone with the public key can ask the database for every row in a table: every customer, every order, every message. Security researchers have found plenty of AI-built apps in exactly this state, often because the app worked perfectly in testing and nobody thought to check.
The two keys, and which is which
- The anon (public) key is fine in the browser, as long as Row Level Security is on for every table.
- The service_role (secret) key ignores all the rules. It's for trusted server code only. If it's anywhere in your app's front end, treat it as leaked: rotate it in Supabase and move whatever needed it to the server.
If you've ever pasted the secret key into a chat with an AI tool to "fix an error", check where it ended up.
Row Level Security in plain English
Row Level Security (RLS) is a switch on each table that says "nobody can read or change anything here unless a rule allows it". Turning it on without adding rules locks the table completely, which is safe but will break your app. The next step is adding policies: rules like "people can only see their own orders" or "anyone can read the product list, but only admins can change it".
A quick way to see where you stand:
- Open your project in the Supabase dashboard and look at your tables. Supabase flags tables that have RLS switched off.
- Check its security advice section, which lists common problems like this.
- For each table with personal data, ask: who should be able to read this, and who should be able to change it? Then check the policies say exactly that.
Other things to check while you're there
- Storage buckets with files people upload should be private unless they really need to be public.
- Database functions that run with extra permissions should check who's calling them.
- If you take payments, the "paid" status should only ever be set by your server after the payment provider confirms it, never by the browser.
None of this is exotic, but it's easy to miss when an app appears to work. If you'd like someone to look over yours before real customers use it, tell us what you've built. There's more on getting it launch-ready in the full launch checklist.
This article is general information, not legal, tax or financial advice. Rules change, so check the official guidance for your own situation.