Supabase、10/30から新規テーブルにGRANT必須化 — 既存プロジェクトで壊れる3つのケースと対処法
はじめに
Supabase は 2026 年 10 月 30 日から、既存プロジェクトも含めて public スキーマの新しいテーブルを Data API(PostgREST / supabase-js)と GraphQL API に自動で公開しなくなります。新規プロジェクトでは 5 月末から既定になっていた変更が、明日から全プロジェクトに広がります。
公式は「既存のテーブルは影響を受けない」と説明していますが、これを読んで安心するのは早いです。マイグレーションを最初から流し直す場面では、過去に作ったテーブルもすべて「新しいテーブル」として作られるからです。この記事では、何が変わるのか、どこで壊れるのか、今日のうちに何を確認すればよいかを整理します。
何が変わるのか
これまで Supabase は、public にテーブルを作ると anon・authenticated・service_role の 3 ロールへ select・insert・update・delete を自動で付与していました。連番(sequence)にも usage と select が付いていました。仕組みは PostgreSQL の default privileges(ALTER DEFAULT PRIVILEGES)で、postgres ロールが作るオブジェクトに既定の権限を付ける設定です。
この自動付与がなくなります。公式 changelog のタイムラインは次のとおりです。
| 日付 | 内容 |
|---|---|
| 2026/4/28 | 新規プロジェクト作成時に「Automatically expose new tables」を外せるようになる |
| 2026/5/18 | pg_graphql が既定で有効化されなくなる |
| 2026/5/30 | 新規プロジェクトでは自動公開しないのが既定に |
| 2026/10/30 | 既存プロジェクトにも適用 |
変更後に GRANT を書かずに作ったテーブルへ supabase-js からアクセスすると、PostgREST はコード 42501、メッセージ permission denied for table your_table を返します。GRANT SELECT ON public.your_table TO anon; のようなヒントも付くため、黙って空配列が返るような分かりにくい壊れ方ではありません。
対象は Data API 経由のアクセスだけです。psql や ORM、接続文字列でアプリサーバーから直接 Postgres に繋いでいる場合は影響を受けません。auth・storage・realtime や独自スキーマも現状の権限のままです。
GRANT と RLS は別の層
この変更を理解するうえで大事なのは、GRANT と RLS が別々の検査だという点です。
PostgreSQL はクエリを受けると、まず「このロールはこのテーブルに触れてよいか」をテーブル単位の権限(GRANT)で調べます。通ったあとで「どの行を見せるか」を RLS のポリシーで絞ります。これまで Supabase では前者が自動で全開になっていたので、開発者は RLS のポリシーだけ書けば動く状態でした。
その結果、「RLS さえ書けば安全」「RLS を書き忘れたテーブルも全ロールから丸見え」という二つの状態が同居していました。RLS の設定漏れでテーブルが anon キーだけで読めてしまう事故は実際に起きています。2025 年には、Lovable で作られたアプリ 170 件超で RLS の不備によりデータを読み書きできた問題が CVE-2025-48757 として登録されました。今回の変更は、入口の権限を既定で閉じ、公開するテーブルを開発者に一つずつ選ばせる方向への転換です。
公式が示す新しいテーブルの書き方は次のとおりです。
grant select on public.your_table to anon;
grant select, insert, update, delete on public.your_table to authenticated;
grant select, insert, update, delete on public.your_table to service_role;
alter table public.your_table enable row level security;
GRANT で入口を開け、RLS で行を絞る。2 行セットで書くのが新しい標準になります。
既存プロジェクトで壊れる 3 つのケース
Zenn の記事(べるべる氏)は、実際に手元で試して次の 3 つを挙げています。
- 10/30 以降に追加するテーブル: 当然
GRANTが要ります。マイグレーションのテンプレートや、AI に書かせているコードが旧来の書き方のままだと、リリース後に 42501 が出ます - マイグレーションの流し直し: 検証環境を新しく作る、障害で本番を作り直す、といった場面です。過去のマイグレーションには
GRANTが書かれていないため、全テーブルが API から使えなくなります。普段は起きず、困ったときにだけ起きるので最も危険なケースです - 一般の実行権限を外した関数:
revoke execute ... from public, anonで絞った関数は、これまで既定権限によってauthenticatedから呼べていました。自動付与がなくなると誰からも呼べなくなります
3 つ目は公式 changelog の本文に手順がありません。本文の SQL はテーブルと連番だけで、関数は設定名に「and functions」と出てくる程度です(GitHub Discussion のコメントでは、メンテナーが grant execute を明示するよう回答しています)。Zenn の筆者も「一番気づきにくいのがこれでした」と書いており、AI に修正を頼む場合も、先に新しい挙動を再現させないとテーブルの GRANT だけで終わって関数を見落とす、と注意しています。
今日やっておくこと
ローカルで先に新しい挙動を再現できます。Supabase CLI v2.102.0 以降で、supabase/config.toml に次を書きます。
[api]
auto_expose_new_tables = false
この状態で supabase db reset してから supabase test db を流すと、ケース 2 を手元で起こせます(db reset はローカルのデータを消すので注意)。Discussion でメンテナーが引用した CLI のコードには「この項目は 2026-10-30 に削除される」とあるので、この設定で試せるのは今日のうちと考えておくのが安全です。
修正は 1 本のマイグレーションにまとめるのが分かりやすいです。
- 既存テーブルに今と同じ権限を明示する。テーブル名を列挙し、
grant ... on all tables in schema publicは使わない。RLS を有効にしていないテーブルまで公開してしまうからです - 実行権限を絞った関数には、呼ぶロールへ
grant executeを書く - default privileges を
revoke allで取り消す。selectなどを個別に外すとTRUNCATEなどが残るためallを使う
さらに pgTAP で「authenticated に select が付いていない public のテーブル」「service_role が実行できない関数」を pg_class / pg_proc から洗い出すテストを置けば、今後の書き忘れも CI で止められます。
Claude Code や Codex にマイグレーションを書かせているなら、CLAUDE.md / AGENTS.md に「テーブル作成時は同じファイルで GRANT と RLS を書く」「all tables in schema public は使わない」と書いておくのが効きます。Zenn の筆者も、AI はしばらく GRANT のない昔の書き方をする可能性が大きいと指摘しています。RLS だけのマイグレーションは長く Supabase の標準的な書き方だったので、明示しないと元に戻りやすい部分です。
なお、公式によればダッシュボードの Security Advisor が影響するテーブルと修正 SQL を表示し、Table Editor には Data API に公開されているかのバッジが出ます。SQL を書く前に一度確認しておくと全体像がつかめます。
まとめ
- 10/30 から、既存プロジェクトでも
publicの新しいテーブルはGRANTを書かないと Data API から 42501 になる - 既存テーブルは動き続けるが、マイグレーションの流し直しでは全テーブルが新規扱いになり全滅する
- 関数の実行権限は公式の告知に明記がなく、見落としやすい
- 対処は「ローカルで
auto_expose_new_tables = falseにして再現 → 権限を明示するマイグレーション → 書き忘れを検出するテスト」の順
RLS だけで守る設計から、GRANT で入口を閉じたうえで RLS を重ねる設計へ移る変更です。手間は増えますが、公開範囲がマイグレーションに明示されることで、レビューで追える状態になります。