Supabase security: why healthy metrics do not prove RLS is safe

The hosted Metrics API proves the platform is running; catalog-only SQL advisors reveal public tables without RLS, broad anon grants, permissive policies, security-definer views, and PII-like column names.

Published 2026-08-148 min readSupabaseSecurity 101PostgreSQLRLS
On this page
  1. Metrics health and data access are different questions
  2. Check RLS and grants together
  3. Connect both surfaces with least privilege

Metrics health and data access are different questions

`pg_up`, connection counts, replication lag, and node metrics tell you whether Supabase is healthy. They cannot tell you whether an `anon` request can read a table that should be private.

The SQL advisor pack queries only `pg_catalog` and `information_schema`. It reports object and column names as labels; it never selects values from tenant tables or views.

Check RLS and grants together

RLS off is dangerous when `anon` or `authenticated` can reach the relation. RLS on can still be effectively public when a policy uses `USING (true)` or `WITH CHECK (true)`.

Check

Check: public tables without RLS

This catalog query lists relation names only. Review every result against its grants and intended API exposure.

SQL✓ Supabase hosted Postgres 17

What to look for: Zero rows for tables that must not be publicly reachable.

Check

Check: permissive policies

Policies that reduce to true may be deliberate for public data; treat them as explicit review items instead of assuming that the RLS toggle alone provides isolation.

SQL✓ Supabase hosted Postgres 17

What to look for: Every row has a documented reason to allow all matching records.

Connect both surfaces with least privilege

The hosted scrape uses outbound HTTPS and a Secret API key. The optional SQL scrape uses a direct port 5432 URI and a dedicated `pg_monitor` login.

Check

Check: privileged Metrics API authentication

HTTP 200 with Prometheus text confirms the project ref, username, and key.

bash✓ Supabase hosted Metrics API 2026-08
curl -fsS -u 'username:<secret>' \
  'https://<project-ref>.supabase.co/customer/v1/privileged/metrics' \
  | head

What to look for: Prometheus exposition containing `pg_up` or connection metrics.

Fix

Fix: use a dedicated catalog-only SQL role

Avoid the dashboard owner or service-role database credentials. `pg_monitor` plus database CONNECT is enough for the bundled catalog and statistics queries.

SQL✓ Supabase hosted Postgres 17
CREATE ROLE prometheus_exporter
  LOGIN PASSWORD 'CHANGE_ME_STRONG_PASSWORD'
  CONNECTION LIMIT 5;
GRANT pg_monitor TO prometheus_exporter;
GRANT CONNECT ON DATABASE postgres TO prometheus_exporter;

Verify it worked: `SET ROLE prometheus_exporter; SELECT count(*) FROM pg_class;` succeeds, while an INSERT into `pg_catalog.pg_class` fails with permission denied.

Note: Use the direct database endpoint on port 5432, not transaction pooler port 6543.

Supabase security: why healthy metrics do not prove RLS is safe — ELK Utilities