HomeBlog › Is your Supabase database exposed
Guide

Is your Supabase database exposed? How to check

Finding a Supabase anon key in a frontend bundle is not a leak — that key is meant to be public. The real question is whether Row Level Security is switched on behind it. If it is not, that public key can read, and sometimes write, every row in your tables.

The mistake that exposes Supabase

Supabase talks to the browser directly using a public anon key, and security is enforced by Row Level Security (RLS) — Postgres policies that decide which rows each request may touch. The intended model is: anon key in the client, RLS on every table, policies that scope access to the right user.

The common failure is shipping the anon key with RLS disabled (or enabled but with no policies). Then the public key is not a locked front door — it is an open one. Anyone who reads your JavaScript bundle gets the key and the project URL, and can query your tables straight from their browser.

How to check, safely

You can confirm exposure with read-only requests — no need to dump or modify data. The goal is a yes/no on access, not a copy of the database.

  1. Find the anon key and project URL — they are in your frontend bundle (this is expected, not the problem).
  2. Make one read request to a table through the Supabase REST endpoint, as an unauthenticated caller.
  3. Read the result: rows returned without any logged-in user means RLS is off and the table is publicly readable. An empty result or a permission error means RLS is doing its job.
  4. Confirm in the dashboard that RLS is enabled on every table — one forgotten table is enough.

The same logic catches the worst case: a service_role key that has leaked into the client. That key bypasses RLS entirely, so if it is anywhere in your frontend, an attacker has full read and write access — rotate it immediately.

Check access, not contents

Proving exposure only requires knowing whether a table is readable — the column names and a row count are enough evidence. Pulling the actual row values just creates a second exposure. Confirm the door is open; do not walk off with what is inside.

How to fix it

  • Enable RLS on every table, no exceptions.
  • Write explicit policies that scope each row to the user who owns it (for example, matching auth.uid()).
  • Never ship the service_role key to the client — keep it server-side only.
  • Re-check after every schema change — a new table ships with RLS off until you turn it on.

Frequently asked questions

Is it safe to put a Supabase anon key in the frontend?

Yes — it is designed to be public. It is safe only if Row Level Security is enabled on every table with policies that restrict access. Without RLS, the public anon key lets anyone read or write those tables.

How do I check if my Supabase database is exposed?

Use the public anon key and project URL to make a read request to a table. If it returns rows with no authenticated user, RLS is off and the table is public. Confirm RLS is on for every table in the dashboard.

What is the difference between the anon key and the service_role key?

The anon key is public and constrained by RLS. The service_role key bypasses RLS entirely and must never reach the client — if it leaks, an attacker gets full read/write access.

Keep reading

Request early access