Skip to content
BugFyx

Guide

Supabase Row Level Security explained for founders

If your app runs on Supabase, Row Level Security decides who may see which data. Here is how it works in plain language, how to check that it is set up right and which mistakes you want to avoid.

By Patrick Willemsen, owner of BugFyx

What is Row Level Security?

Supabase is a database with everything around it: logins, file storage and a ready-made way to talk to the database from your website. That last part is what makes it so fast to build with, and it is also why Lovable, Bolt and other AI tools like to use it. Your app talks to the database directly from the browser, without a server of its own in between.

But if the browser talks to the database directly, who keeps an eye on what someone is allowed to request? That is what Row Level Security does. Think of your database as a filing cabinet full of folders. Without RLS, anyone who reaches the cabinet can take any folder. With RLS, someone stands by the cabinet and checks for each folder whether you are allowed to see it: only your own invoices, only your own team’s projects.

The rules that decide who may do what are called policies. RLS is the switch that says ‘check the rules’, and the policies are the rules themselves. You need both.

Two keys: the public one and the secret one

Every Supabase project has two kinds of keys. In older projects they are called anon and service_role, in newer projects publishable and secret. The difference matters a lot.

The public key sits in your website’s code. Anyone who visits your site can find it, and that is not a mistake: that is how Supabase is designed. With that key, someone can only do what your policies allow. But that is the catch. If RLS is off on a table, there is nothing to allow or refuse, and anyone with the public key can request that entire table. An incident with apps built with Lovable, registered in 2025 as CVE-2025-48757, was about exactly this kind of overly loose database rules.

The secret key bypasses RLS entirely. It is meant for tasks on a server, such as processing a payment or carrying out an admin action. The Supabase documentation is clear: use it only on a server and never put it in the browser or anywhere else users can see it.

Public key (anon or publishable)
Sits in your website and may be visible. Can only do what your policies allow, so it is only safe with RLS switched on.
Secret key (service_role or secret)
Bypasses every policy. Belongs on a server only, never in the browser.
Policy
A rule on a table that says who may read, add, change or delete which rows.

How to check whether RLS is on

You do not need to be a developer to see whether RLS is on. The Supabase dashboard shows it per table, and warns you about the best-known problems itself. Go through these steps:

  1. 1Log in to the Supabase dashboard and open your app’s project.
  2. 2Open the Table Editor and go through every table. Tables without RLS get a clear warning in the dashboard.
  3. 3Open the policies overview and check which rules each table has. A table with RLS on but no policies is closed to the public key. A policy that allows everything for everyone is as open as RLS off.
  4. 4Open the Security Advisor. Among other things, it warns about tables without RLS and views that bypass RLS.
  5. 5Do not forget file storage (Storage): check which buckets are public and which policies apply.

What is a policy? An example in plain language

Say your app is a simple project planner. Each project is a row in the projects table, with a column for its owner. A good policy for reading says, in plain language: ‘a user may only see a row if they are its owner’. In database terms, that is a comparison between the logged-in user, auth.uid(), and the owner column.

You create separate policies for adding, changing and deleting. When adding, for example, you want to check that someone can only put a new project in their own name, not in someone else’s. For deleting, you may want only the owner to be allowed, while team members can still make changes.

So a policy is not a technical detail but a business decision: who may do what with which data? Write those rules down in plain sentences first. Then a developer, or your AI tool, can turn them into policies, and you can check that they are right.

Six common mistakes

Most RLS problems do not arise because someone does not want security, but because the app ‘suddenly showed nothing’ and someone, human or AI, picked the quickest fix. These are the mistakes you see most often:

  1. 1RLS is off. A table you create with SQL does not have RLS switched on automatically in PostgreSQL. Forget it, and you have a table that is open to anyone with the public key.
  2. 2RLS is on, but with no policies. Then nobody sees anything through the public key. That is safe, but the app does not work, and it is tempting to just switch RLS off again.
  3. 3A policy that allows everything. A rule like ‘true’, meaning ‘anyone may do anything’, makes the warning in the dashboard go away but protects nothing.
  4. 4The secret key in the browser. For example because it is in a setting starting with VITE_ or NEXT_PUBLIC_. Settings like that are deliberately put into the code for the browser.
  5. 5Forgetting file storage. Files in Storage have their own policies. A bucket set to public can be read by anyone who has the link.
  6. 6Views that bypass RLS. A view is a saved query. By default it runs with the rights of its creator and does not follow the policies of the underlying tables. On PostgreSQL 15 and later, the security_invoker setting fixes that.

Testing as a different user

Writing policies is one thing, knowing they work is another. So always test the way a real user uses the app. Create two test accounts, add some data with both and see whether one account can see or change anything belonging to the other. Then try again while logged out.

Be careful with the SQL editor in the dashboard. By default it runs with the rights of the database administrator, and policies do not apply to that role. A query that returns every row there tells you nothing about what a user sees. Supabase does have a feature to act as a specific user or role, in the Table Editor and the SQL editor. Use that, or test through the app itself.

Do not run this test once, but after every change to your database. A new table that your AI tool creates needs new policies too.

When to get help

If your app has a handful of tables and no sensitive data, the steps above will get you a long way. If you work with personal data, payments or company data, or your app has several kinds of users, such as customers, staff and admins, you want someone who can read policies to go through them once.

That is what I do in the App Check. For €600 (excl. VAT) I review your database, policies, keys and file storage within 5 working days, together with the rest of your app. You get a report in plain language with the risks in red, orange and green, and we go through it together in 30 minutes. If you then start a maintenance plan within 30 days, I credit the €600 in full. In that plan I look after your app from then on, with monitoring, backups and a daily check for known vulnerabilities.

Frequently asked questions

Is it bad that my public Supabase key can be seen in the browser?
No, that is how Supabase is designed. The key can only do what your policies allow. It only becomes a problem when RLS is off on a table or a policy allows everything for everyone.
My app shows nothing after I switched RLS on. What now?
That is expected: with RLS on and no policies, a table is closed. Do not switch RLS off again, but add policies that describe exactly who may read and change which rows, and test them with two accounts.
Does RLS also apply to files that users upload?
Yes, but separately. Files in Supabase Storage are protected by policies on the storage itself. A bucket set to public can be read by anyone with the link, even when your tables are well secured.
Can my AI tool write the policies for me?
Yes, an AI tool can make a decent first version, especially if you describe the rules in plain sentences first. Always test them yourself as a real user afterwards, because a policy that looks right can still be too loose.

Make sure your database is locked down

In the App Check I review your RLS, policies, keys and file storage, together with the rest of your app. Within 5 working days, for a fixed price of €600.

Sources

Know where your app stands within 5 working days

With the App Check you get a clear report on security, database, hosting and backups for €600. If you take a maintenance plan afterwards, I credit the €600 in full.

  • Fixed price
  • Plain-language report
  • Within 5 working days