Check what your Supabase project shows the world
The anon key is supposed to be public. The protection is Row Level Security — and the one key that bypasses it must never reach the browser. We check both from the outside, the way an attacker would, without touching your data.
Free, no sign-up. The basic check makes only the requests an ordinary browser would.
What we check
- CriticalFree check
service_role or sb_secret key in the frontend
Both bypass Row Level Security completely. We search the page and its scripts and show the key redacted, with the file it’s in.
- CriticalPro
Tables readable with the anon key
After domain verification we send one HEAD request per table the app uses and report how many rows anyone could read. Contents are never fetched.
- NotePro
Tables protected by RLS
Tables that return zero rows to the anon key are listed too, so you can see the policy is doing its job.
- NoteFree check
Which project the app talks to
The Supabase project and key type found in the page — the anon or publishable key is expected and is not a finding.
Check it yourself in five minutes
Everything we check from the outside you can check from your own dashboard and terminal. Do it on your own project only.
List tables without Row Level Security
Supabase dashboard → SQL Editor. Any public table with rowsecurity = false is readable by the anon key if the default grants are in place. The Security Advisor (Advisors → Security) shows the same list.
select tablename, rowsecurity from pg_tables where schemaname = 'public' order by rowsecurity, tablename;
Try it as a stranger
Use the anon key from your site — the same one every visitor has. If this returns rows, so does it for anyone.
curl 'https://<project>.supabase.co/rest/v1/<table>?select=*&limit=1' \ -H "apikey: <anon key>" \ -H "Authorization: Bearer <anon key>"
Make sure the secret key never reached the browser
On your live site open DevTools, search all loaded files (Cmd/Ctrl + Option/Alt + F) for service_role and sb_secret_. Any hit means: rotate the key in Project Settings → API, then move that code to an Edge Function.
Turn RLS on and add a policy
Enabling RLS without a policy blocks everything — add one that matches how the table is used. For a table of user profiles:
alter table public.profiles enable row level security; create policy "read own profile" on public.profiles for select using (auth.uid() = id);
A fix your AI can apply
The report’s prompt names the tables and the exact problem, and asks your AI to enable RLS with policies that match how the app uses each table.
Fix these issues on app.example.com. 1. The table "profiles" returns 1,240 rows to the anon key. Enable Row Level Security on it and add a policy so users can select only their own row (auth.uid() = id). 2. The table "orders" returns 87 rows to the anon key. Enable RLS; allow select only to the order's owner. When done, re-check: https://scanyoursite.app/en?check=app.example.com
A new table is a new risk
Every migration can add a table without a policy. With Pro the table check runs on every scheduled scan, and you hear about a newly open table the same day.
See plansQuestions
Why do I need to verify my domain for the table check?
Querying a database goes beyond what a browser does on a page, so we only do it for the owner. Verification takes a DNS record, a file or a meta tag.
Do you store my data?
No. We count rows with a HEAD request and keep only the number and the table name.
Is the anon key a leak?
No. It is public by design. We report it as a note, not a problem.
Paste your URL. Get the list in about a minute.
Every finding comes with what it means for you and how to fix it — or a prompt your AI coding tool can apply.
Free, no sign-up. The basic check makes only the requests an ordinary browser would.