SupabaseでSSL接続を強制する手順と、設定変更時のデータベース再起動で起きること

対象: アプリは動いているけれど、「通信の暗号化って、私のDBはどうなってるの?」が気になっている方
所要時間: 15〜20分 / 使うもの: Supabaseの無料プラン、ターミナル(psql が入っていると確認がしやすいです)
続きの記事(このしくみを、実際のWebアプリにしてみた): SSLとDB再起動を「見える化」するステータスページをつくってみた:暗号化されているかと、止まっていた時間を1画面で
ビジネス編(やさしい解説): AIでアプリをつくる人が見落としがちな「通信の暗号化」:データの通り道に鍵をかけて顧客の信頼を守る

「SSLを強制」というスイッチがダッシュボードにあるのは知っていました。でも、押すとなんだかデータベースが再起動するらしい。本番で押したら、アプリが止まるのでは…? そう思うと、なかなか押せませんでした。

そこで今回は、デモ用のプロジェクトで実際に押して、何が起きるかを全部見てみました。この記事はその記録です。

設定の画面(オフの状態)

この記事でやること

  1. 「SSLを強制」する前の状態を、接続テストで確かめる
  2. スイッチをオンにする(再起動の確認ダイアログも見る)
  3. 再起動で実際にどのくらい止まったかを調べる
  4. 強制した後の状態を、同じ接続テストで確かめる
  5. アプリ側(Edge Function)が引き続き動くか確認する

0. 先に伝えておきたいこと(正直ベース)

  • 試したのは、作ったばかりの空に近い無料プランのデモプロジェクトです(一番小さいサイズ)。データが多い本番では、再起動にかかる時間は変わるはずです。ここで測った数字は「目安の一例」として読んでください。
  • 接続テストには本物のDBパスワードは使いません。わざと間違ったパスワードで接続を試して、「どの段階で断られるか」を見ます。パスワードを知らなくても、強制されているかどうかは分かります。
  • 私の環境ではDBの直接接続先(IPv6)に届かなかったので、IPv4でつながる「Session pooler」 という接続口を使いました。接続口が違っても、SSLの強制は同じように効いていました。

1. 「強制する前」の状態を確かめる

まず、強制する前にテストします。psql で「SSLを使わない接続(sslmode=disable)」と「SSLを使う接続(sslmode=require)」を、間違ったパスワードで試します。

# 接続先は、ダッシュボードの Connect → Session pooler に出ているもの
HOST=aws-0-<リージョン>.pooler.supabase.com
USER=postgres.<プロジェクトREF>

for m in disable require; do
  printf "sslmode=%-8s -> " $m
  PGPASSWORD=wrong-on-purpose psql 
    "host=$HOST port=5432 dbname=postgres user=$USER sslmode=$m" 
    -c "select 1" 2>&1 | head -1
done

結果はこうでした(長い行は省略しています)。

sslmode=disable  -> ... FATAL:  password authentication failed for user "postgres"
sslmode=require  -> ... FATAL:  password authentication failed for user "postgres"

どちらも「パスワードが違います」まで進みました。 つまり、暗号化なしの接続でも、玄関までは入れてもらえている状態です。パスワードが合っていれば、そのまま暗号化なしでデータがやり取りできてしまいます。

2. スイッチをオンにする

Project → Database → Settings を開き、SSL configuration の Enforce SSL on incoming connections をオンにします。

すると、確認ダイアログが出ました。

再起動についての確認ダイアログ

Updating SSL enforcement involves a brief downtime
A database restart is required for SSL enforcement changes to take place, and this involves a few minutes of downtime.

「数分のダウンタイム」と書いてあります。ここで私は少し緊張しました。Enable SSL を押します。

設定画面はこうなりました。

スイッチがオンになった

3. 再起動でどのくらい止まった?

ここで、私は最初に間違った測り方をしました。

つまずき:「止まらなかった」と勘違いした

スイッチを押したあと、3秒おきにさっきの接続テスト(SSLあり、間違ったパスワード)を回し続けました。ずっと password authentication failed(=応答している)のまま。「あれ、止まってない?」と思いました。

でも、これは測り方がまずかったようです。この接続口(pooler)は、間違ったパスワードの拒否を、データベースの中身まで見に行かずに返せるみたいで、データベース本体が再起動していても同じ答えを返し続けていました(これは私の推測です。確かめてはいません)。

本当に再起動したかを、データベース自身に聞く

そこでSQL Editorで、Postgresがいつ起動したかを聞いてみました。

select pg_postmaster_start_time() as postgres_started_at, now() as checked_at;
Postgresの起動時刻

Postgresの起動時刻は 06:44:55 UTC(日本時間 15:44:55)。私が Enable SSL を押したのは日本時間 15:44:49 だったので、押した約6秒後に再起動していました。

止まっていた時間を、cronの記録から見る

もう一つ手がかりがあります。前の記事で作った「10秒ごとのcronジョブ」は、データベースの中で動いています。なので、データベースが止まっている間は実行されないはずです。実行履歴を見てみました。

select to_char(start_time at time zone 'Asia/Tokyo', 'HH24:MI:SS') as started_jst, status
from cron.job_run_details
where start_time between '2026-10-13 06:44:30+00' and '2026-10-13 06:45:40+00'
order by start_time;
cronの実行履歴に空白がある

15:44:45 の次が 15:45:06。ほんとうは10秒ごとなので、15:44:55 と 15:45:05 の2回分が抜けています。つまり、約20秒間、データベースが仕事をしていなかったようです。

まとめると、私のデモでは:

見たもの 結果
ダイアログの案内 「数分のダウンタイム」
Postgresの起動時刻 ボタンを押した約6秒後
cronの空白 約20秒

ダイアログは「数分」と、安全側に書かれているようです。ドキュメントでは、SSL強制の変更は「fast database reboot」(データベースだけの高速な再起動)で、「小さなプロジェクトでは通常数秒、大きいと長くなることがある」とされています(同じページの別の箇所には「数分」とも書いてあり、表現が揃っていません)。私の約20秒は、これと合っています。ただし、小さな空のDBでの1回の結果なので、本番のデータ量・接続数では違うはずです。「数秒〜数十秒で戻ることもあるが、数分見ておく」くらいの心づもりが安全そうです。

追記:このあと、ダッシュボードの Restart project を2回試したところ、cronの空白は1分17秒と1分29秒でした(外から呼び続けると、応答が戻るまで約70秒)。最初は、上の「約20秒」は運が良かっただけかと思いました。でも調べると、Restart project はデータベースだけでなくプロジェクト全体の再起動で、SSL強制のスイッチ(データベースだけの再起動)とは別の操作でした(Restart project の横の ▼ に「Fast database reboot」があり、「データベースだけ、全体の再起動よりダウンタイムが短い」という説明が出ます)。なので、この記事の約20秒と、Restart project の1分以上は、比べられる数字ではありません。詳しくは、続きの記事にまとめました。

4. 「強制した後」の状態を確かめる

再起動のあと、さっきと同じテストを回します。

sslmode=disable  -> ... FATAL:  (ESSLREQUIRED) SSL connection is required for user: postgres
sslmode=require  -> ... FATAL:  password authentication failed for user "postgres"

変わったのは1行目です。

  • 強制前: disable でも「パスワードが違います」まで進めた
  • 強制後: disable は、パスワードを聞かれる前に SSL connection is required と断られた

require(SSLあり)のほうは、今までどおり認証まで進みます。これで「暗号化なしでは玄関にも入れない」状態になりました。

5. アプリは引き続き動く?

前の記事で作った仕組み(Edge Function + 自動エンベディング)が、再起動と強制のあとも動くか確かめました。

  • 検索用のEdge Function(search)を呼ぶ → 普通に結果が返ってきました
  • 新しい行を1件追加して45秒待つ → embedding が自動で入りました(確認後、その行は削除しました)

Edge FunctionからのDB接続は、私は何も設定していません。ドキュメントによると、デプロイされたEdge FunctionはDBへSSLで接続するよう最初から設定されているそうです。実際に動いたので、その説明と合っていました。

気をつけたいこと

  • 再起動が入るので、本番ではお客さまが少ない時間に。社内への事前告知もしておくと安心です。
  • 暗号化なしでつないでいる古い道具・スクリプトがないか、スイッチを押す前に点検したほうが良さそうです。強制後は、それらは接続できなくなります。
  • sslmode=require は暗号化はしますが、接続先が本物かの確認まではしません。ドキュメントでは、強制するなら verify-full が推奨されていて、Database Settingsからダウンロードできる証明書を使います。私はここまで試していません。
  • HTTPのAPI(REST、Storage、Auth など)は、もともと常にSSL必須だと、ドキュメントに書かれていました(これも私は確かめていません)。今回のスイッチは、Postgresへの直接接続に効くものです。

まとめ

強制の前 強制の後
sslmode=disable パスワード確認まで進める パスワードの前に拒否される
sslmode=require 進める 進める
  • スイッチを押すと、データベースが再起動します(私のデモでは約20秒の空白)。
  • 押す前に、暗号化なしの接続を使っている道具がないか確認しておくと安心です。
  • 効果の確認は、間違ったパスワードでできます。本物のパスワードを出さなくて済むのがうれしいところでした。

次は、「ログインできる人」と「見ていい人」をテーブルの単位で分ける話(RLS)に進みます。