Skip to content

23 tools · 104 fixes · 29 issue guides · Sources: GitHub, Supabase, OWASP, Vercel + 28

Free check

Supabase exposure checker

Paste your Supabase project URL, the public anon key, and the table names from the Table Editor or your migrations. The check runs in your browser. It is not a full security audit.

Only test projects you own or are authorized to test.

This check runs entirely in your browser. Nothing is sent to the ShipSecurely server, logged, or stored. It sends read requests to the project URL you enter: one row from each table you name, and the buckets visible to that key. It may also try the Data API catalog, which Supabase usually refuses for the anon key. It never inserts, updates, or deletes.

It is not a full security audit. A table that returns a row is exposed to the anon key. A table that returns nothing may still be open to a signed-in user, a view, or a storage policy this check does not see. The guide for the October 30, 2026 grant change is New Supabase tables need an explicit grant.

One name per line, or separated by commas. Copy them from the Table Editor, or from the create table lines in your migrations. The anon key can no longer list every table, so names you paste are what get checked. If the catalog happens to answer, extra names are added after yours.

The anon key, and the newer publishable key, are meant to be public. They only reach what your grants and row level security allow. The secret key and the legacy service_role key are not public. Do not paste those.

Questions

Does this send my key to ShipSecurely?

No. The project URL and the key are sent from your browser to your Supabase project only. ShipSecurely does not receive them, log them, or store them.

What is the anon key, and why is it public?

The anon key is the client key Supabase expects in a browser. The newer publishable key, which starts with sb_publishable_, is the same idea. Anyone can read it from your app. It is not a password. Grants decide whether that key can touch a table, and row level security decides which rows it can see. The secret key and the service_role key are different. Those bypass row level security and must stay on a server.

Why can no rows returned mean two different things?

An empty list does not prove a policy hid the rows. The table may simply have no rows. A 401, 403, or Postgres 42501 is a different result, and this check calls it inconclusive: select=* can be refused while a single column is still readable.

Why do I type the table names?

Supabase stopped returning the OpenAPI catalog to the anon key on 11 March 2026 for new projects and on 8 April 2026 for existing projects. That request comes back as “Access to schema is forbidden.” Names from the Table Editor, or from the create table lines in your migrations, are the check. If the catalog does answer, those extra names are added after yours.

Is this a security audit?

No. It is a read-only look at what the anon key can select, one row at a time, plus the buckets that key can list. An empty bucket list does not rule out public files. It does not review policies, auth, functions, or storage object rules. A quiet result is not a clean bill of health.

The weekly fix

The weekly fix: 3 real security mistakes in vibe-coded apps and how to fix them, every Tuesday.

One email, every Tuesday. A welcome note follows the first signup.