対象: アプリは動いているけれど、「通信の暗号化って、私のDBはどうなってるの?」が気になっている方
所要時間: 15〜20分 / 使うもの: Supabaseの無料プラン、ターミナル(psqlが入っていると確認がしやすいです)
続きの記事(このしくみを、実際のWebアプリにしてみた): https://qiita.com/tomozilla/items/b397e16fe1f249b4d29e
ビジネス編(やさしい解説・note): https://note.com/tomozilla/n/n11ff9cfb7b67
「SSLを強制」というスイッチがダッシュボードにあるのは知っていました。でも、押すとなんだかデータベースが再起動するらしい。本番で押したら、アプリが止まるのでは…?
そう思うと、なかなか押せませんでした。
そこで今回は、デモ用のプロジェクトで実際に押して、何が起きるかを全部見てみました。この記事はその記録です。
この記事でやること
- 「SSLを強制」する前の状態を、接続テストで確かめる
- スイッチをオンにする(再起動の確認ダイアログも見る)
- 再起動で実際にどのくらい止まったかを調べる
- 強制した後の状態を、同じ接続テストで確かめる
- アプリ側(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の起動時刻は 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;
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)に進みます。
この記事のスクリーンショットと計測は、記事用に作った無料プランのデモプロジェクトで取ったものです。




