DEV Community

Cenk KURTOĞLU
Cenk KURTOĞLU

Posted on

Your Supabase App Breaks on October 30. Here's the 30-Minute Preparation Guide.

On October 30, 2026, Supabase stops auto-granting anon, authenticated, and service_role access to newly created tables in existing projects (changelog 45329). Any table created after that date, in a migration or from the SQL editor, will return 42501 permission denied from the Data API until an explicit GRANT is added.

Update: I wrote the exact copy-paste fix for the errors this change produces — "Supabase permission denied After October 30: The Exact Fix (Copy-Paste SQL)" — including the 60-second check query and GRANT templates for read-only, client-write, and service-role-only apps.
If your app creates tables at runtime — or your team runs migrations late — your users find this error before you do. This guide gets you ready in 30 minutes.

The 5-minute audit: how exposed are you?

Run this in your Supabase SQL editor today:

-- Which tables currently rely on auto-grants?
SELECT c.relname, c.relrowsecurity, 
       has_table_privilege('anon', c.oid, 'SELECT') as anon_select,
       has_table_privilege('authenticated', c.oid, 'SELECT') as auth_select
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r'
ORDER BY c.relname;
Enter fullscreen mode Exit fullscreen mode

If anon_select or auth_select is true for tables you care about, those grants came from the auto-grant system — and new tables after Oct 30 won't get them.

The 3 things that break

1. Migrations that create tables

If your migration files have CREATE TABLE without a matching GRANT, those tables will be invisible to the API after Oct 30.

Fix (in the migration, right after CREATE TABLE):

CREATE TABLE public.my_table (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  data text
);

-- Add this immediately (the part that was automatic before):
GRANT SELECT, INSERT, UPDATE, DELETE ON public.my_table TO authenticated;
GRANT USAGE ON SEQUENCE public.my_table_id_seq TO authenticated;  -- if using serial columns
Enter fullscreen mode Exit fullscreen mode

2. Runtime table creation

Tenant-per-table schemes, admin "add table" flows, and any code that calls CREATE TABLE at runtime. These are the most dangerous — they break silently until a user hits the new table.

Fix (in the function that creates tables):

CREATE OR REPLACE FUNCTION create_tenant_table(tenant_id TEXT)
RETURNS void LANGUAGE plpgsql SECURITY DEFINER AS $$
BEGIN
  EXECUTE 'CREATE TABLE tenant_' || tenant_id || ' (id uuid PRIMARY KEY, data text)';
  EXECUTE 'GRANT SELECT, INSERT, UPDATE, DELETE ON tenant_' || tenant_id || ' TO authenticated';
END;
$$;
Enter fullscreen mode Exit fullscreen mode

3. ALTER DEFAULT PRIVILEGES in migrations

You might be tempted to fix this with default privileges. On hosted Supabase, this doesn't work — the postgres role can't alter default privileges for Supabase-managed roles (42501 permission denied). Don't burn an afternoon on this path.

The GRANT trap

When adding GRANT statements, don't over-grant. The most common mistake I see in audits:

-- BAD: this re-opens the exposure the change was designed to prevent
GRANT ALL ON public.my_table TO anon, authenticated;
Enter fullscreen mode Exit fullscreen mode

Instead:

-- GOOD: explicit privileges per role
GRANT SELECT, INSERT, UPDATE, DELETE ON public.my_table TO authenticated;
-- Only add anon grants if the table is genuinely public (marketing content, public profiles)
Enter fullscreen mode Exit fullscreen mode

The 30-minute checklist

  1. Run the audit query (above) — inventory your current grants (2 min)
  2. Search migrations for CREATE TABLE — every file that creates a table without a GRANT (5 min)
  3. Add GRANT statements to each migration (10 min)
  4. Grep for runtime CREATE TABLE — in functions, triggers, or application code (5 min)
  5. Test in a staging project — create a table via migration and verify the Data API can access it (5 min)
  6. Document the change for your team — especially if different people write migrations (3 min)

What Supabase will NOT do for you

  • They won't auto-add grants retroactively after the change
  • They won't warn you when a new table is created without grants
  • They won't provide a Dashboard workflow for managing default privileges
  • The postgres role can't ALTER DEFAULT PRIVIGES for their managed roles

Automate the check

Run this monthly as a safety net:

-- Tables that are accessible to authenticated
SELECT relname FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'public' AND c.relkind = 'r'
  AND has_table_privilege('authenticated', c.oid, 'SELECT');
Enter fullscreen mode Exit fullscreen mode

Or use the free client-side checker at rls.cenkkurtoglu.com — it scans your public repos for exactly these patterns (migrations without grants, committed secrets, permissive RLS policies) entirely in your browser, nothing stored.


This change is a good thing — it closes a real security exposure (tables accidentally exposed to the anon key). But it's a breaking change, and the teams it hits hardest are the ones running migrations after October 30 without testing. Don't be that team.

I audit Supabase/Postgres setups for production apps — if you'd rather have someone run this entire preparation and hand you a readiness report, that's what I do. The checklist above works on its own either way.


Ready-to-use templates: Supabase October 30 Migration Kit ($29) — 5 migration patterns, audit script, testing script, 15-minute checklist.


Related: if your baselines and dumps re-create Supabase's most dangerous default — every new table born granted to anon — I wrote up the catalog-driven, self-healing fix here: Supabase's Default Privileges Expose Every New Table. Here's the Self-Healing Fix.

Top comments (0)