Supabase audit logs: what they record—and what they miss
Supabase has several logging surfaces, but they answer different security questions. This guide separates Platform Audit Logs, Auth Audit Logs, the Logs Explorer, and database-level auditing so you can choose the trail you actually need.
Supabase Platform Audit Logs record control-plane actions performed by organization members in the dashboard and Platform API. Auth Audit Logs separately record authentication events such as logins, signups, password changes, token refreshes, and logouts.
Operational activity appears in the Logs Explorer, which exposes logs from the API, Auth, Postgres, Storage, Realtime, and Edge Functions. None of those surfaces is a row-level change history for your application tables; tracking who changed a row requires pgAudit or an audit trigger configured before the change occurs.
Four audit and logging surfaces
The names overlap, but the records do not. Start with the question you need answered, then choose the surface that can produce that evidence.
| Surface | Records | Answers |
|---|---|---|
| Platform Audit Logs | Dashboard and Platform API actions by organization members | Who changed the project, and when |
| Auth Audit Logs | Signups, logins, password changes, token refreshes, and logouts | What happened in a user's authentication lifecycle |
| Logs Explorer | API, auth, Postgres, Edge Function and storage traffic — edge_logs, auth_logs, postgres_logs and the rest | What requests hit the project |
| Audit trail in the database | Nothing, until you set up pgAudit or triggers | Which rows changed, and how |
Platform Audit Logs watch your team, not your application traffic
Supabase Platform Audit Logs automatically record dashboard and Platform API actions by organization members. The record includes the actor, action, target, timestamp, and related metadata. Supabase currently makes this surface available on Team and Enterprise plans.
But it is scoped to the control plane. Traffic that arrives with an API key never touches it. Your application does not appear in it. Neither does a leaked service_role key reading every row of every table, nor a coding agent connected to production, nor an anonymous request that Row Level Security should have filtered and did not. Those are all data-plane events, and the audit log is structurally blind to them.
This matters because the surfaces can tell one story between them. Platform Audit Logs can show a project setting or Edge Function changing on Tuesday; the Logs Explorer can show the resulting application traffic on Wednesday. Nothing in either surface automatically turns those separate records into one investigation.
Auth Audit Logs record the authentication lifecycle
Supabase Auth Audit Logs capture signups, logins, password changes and resets, verification events, token refreshes, and logouts. Supabase stores them externally by default and lets you optionally write them to auth.audit_log_entries for SQL access.
They answer identity questions that Platform Audit Logs do not, but they still do not show which application row a signed-in user later inserted, updated, or deleted. Authentication and data modification are separate events; joining them requires your own application context or database audit design.
How Defencecore spots a hacked or abused Supabase project
Defencecore watches for warning signs in the logs your project already creates. It does not wait for someone to open the dashboard and search manually. New events are checked against security rules, and related events are grouped into one incident when a rule matches.
Here are three examples in simple terms:
- Row Level Security is turned off. If RLS is disabled on a table, data that was protected may become reachable through the API. Defencecore names the table and shows the log entry that triggered the alert.
- Someone keeps guessing passwords. Many failed sign-ins from the same address in a short time look different from one user mistyping a password. Defencecore groups the attempts and reports the address, count, and time window.
- Someone keeps trying actions an RLS policy blocks.A blocked request means the policy worked, but repeated blocked writes can still show abuse, a broken client, or an attempt to reach another user's data.
Each incident explains what happened, which project resource was involved, and what to check first. It also keeps the matching log lines so you can confirm the finding instead of trusting a black-box score.
There is an important limit: Defencecore can only detect activity that leaves a useful signal in the available Supabase logs. It is a security monitor, not a guarantee that every possible breach will be visible.
Postgres does not record who deleted the rows
This is the assumption that costs people the most time. postgres_logs exists, so it feels reasonable to expect that a destructive statement left a trace in it. By default it did not: successful INSERT, UPDATE, and DELETE statements are not a row-level change history, and Supabase disables Postgres connection logging on new projects until you enable it. When the rows are gone, the missing audit configuration cannot be applied retroactively.
Two ways to fix that, with different costs:
- pgAudit logs statements as they execute, into the Postgres log stream where the Logs Explorer can query them. It is thorough and it is loud — auditing writes across a busy schema produces a large volume of log lines, and the retention window applies to all of them.
- An audit table written by your own triggers keeps the before and after state of each row as data in your database, where you control the schema and retention. The trade-off is ownership: the trigger, permissions, storage growth, and cleanup policy are now application code you must test and maintain.
-- Enable pgAudit first, then choose the narrowest useful statement classes.
alter role postgres set pgaudit.log = 'function, write, ddl';
-- API traffic connects through the authenticator role.
alter role authenticator set pgaudit.log = 'write';
-- Apply the settings with a fast reboot, then inspect Postgres Logs.Note what neither approach gives you on its own: identity. A log line names the Postgres role that ran the statement, which for anything arriving through PostgREST is authenticated or anon, not a person. Tying a change back to an end user means capturing auth.uid() in the audit record yourself — and a statement run with the service_role key has no user to capture, which is the point at which an audit trail stops being able to answer the question at all.
Retention depends on the plan and destination
Supabase documents plan-dependent retention for Platform Audit Logs and the Logs Explorer. For project logs, a log drain can stream Postgres, Auth, Storage, Edge Function, Realtime, and API Gateway logs to an external observability or archival destination. That changes the retention boundary, but it also adds a separate service and usage cost.
Upgrading buys a longer window and a larger bill without changing the shape of the problem — the log retention guide works through why paying to keep routine traffic is the wrong lever, and what to keep instead.
What an audit trail is actually for
An audit log you read after an incident is forensics. It is worth having, and it is the wrong instrument for the job everyone actually wants done, which is finding out on Wednesday rather than in November.
Defencecore reads your Supabase logs continuously, normalizes them, and applies detection rules as events arrive — a spike in RLS denials after a migration, authentication patterns that look like credential stuffing, a read volume that does not match the number of users online. When a rule matches, it opens an incident and attaches the log lines that triggered it. Those stay readable after the originals age out of Supabase, so the evidence outlives the retention window that would otherwise have destroyed it. Access is read-only: it can explain what happened to your database, never cause it.
Evaluating how widely the platform is used? Browse the curated directory of companies using Supabase in production.
For the operational workflow, compare manual Supabase log review with Defencecore.
Frequently asked questions
- Does Supabase have audit logs?
- Yes. Platform Audit Logs record dashboard and Platform API actions by organization members, while Auth Audit Logs record authentication events such as logins, signups, password changes, token refreshes, and logouts. Neither is a row-level history of INSERT, UPDATE, and DELETE activity in your application tables.
- Do Supabase logs show who deleted a row?
- Not by default. Postgres does not log the text of ordinary DML statements unless it is configured to, and even then a log line records the statement and the connecting role rather than the end user behind it. Answering 'who deleted this row' requires either pgAudit or an audit table populated by triggers, set up before the deletion happens.
- What is the difference between Supabase audit logs and the Logs Explorer?
- Platform Audit Logs cover control-plane actions made by organization members. Auth Audit Logs cover authentication events. The Logs Explorer exposes operational logs from the API, Auth, Postgres, Storage, Realtime, and Edge Functions. A dashboard user changing a project setting belongs in Platform Audit Logs; requests reaching the application belong in the Logs Explorer.
- How long are Supabase audit logs kept?
- Supabase states that Platform Audit Log and Logs Explorer retention depend on your plan. Auth Audit Logs can also be written to auth.audit_log_entries in Postgres. For longer retention, Supabase offers log drains that stream project logs to an external destination; check the current pricing and documentation before choosing a retention design.
- Can Defencecore tell me if my Supabase project was hacked?
- Defencecore detects warning signs in the logs Supabase already creates. It can flag Row Level Security being turned off, repeated failed sign-ins from one address, and repeated requests blocked by an RLS policy. It opens an incident with the affected table or address, the matching log lines, and a first action to take. No log monitor can prove that every possible attack was seen, especially when the activity was never logged.