Cloudflare Tunnelで自宅サーバーを安全公開:ポート開放なしの実践手順
深夜2時。寝落ちしかけた僕の手元のRaspberry Pi 5で、ふと思いついた。「このダッシュボード、外から見られるようにしたい」と。ルーターのポートを開ける気はさらさらない。自宅のIPを晒したくもない。動的DNSの設定で過去に3回詰んだ経験がある。
そこで導入したのがCloudflare Tunnelだった。半年運用して、ポート開放なしで自宅のRaspberry Piをインターネット越しに扱えるようになった。本記事では、実際に自宅のRaspberry Pi 5上でcloudflaredを常駐させ、Zero Trustで制限をかけるまでの手順と、運用で躓いたポイントを書き残しておく。
Cloudflare Tunnelとは(超ざっくり)
Cloudflareが提供する、アウトバウンド接続のみでサーバーを外部公開する仕組み。自宅側からCloudflareのエッジに常時接続(gRPC over HTTPS)を張り、外からのリクエストはCloudflare経由でそのトンネルを通って入ってくる。
つまりルーターの穴あけ(ポートフォワーディング)は一切不要。固定IPも要らない。CGNATの下でも動く。料金は無料プランでもほぼ使える。これが強烈に刺さった。
- ポート開放不要 → 自宅ルーターが無防備にならない
- 公開IPが露出しない → DDoSの標的にされにくい
- TLS終端はCloudflare側 → 証明書の更新で悩まない
- Zero Trustでアクセス制御 → Google認証やメールOTPで簡単にゲートがかけられる
以前のやり方との比較
以前、自宅サーバー運用の取り組みをRaspberry Pi 5を家庭内サーバーとして運用する実践Tipsでまとめた。そこではTailscaleとReverse Proxyで閉じていたが、身内以外にも共有したい場面が出てきて、公開の必要が生じた。Cloudflare Tunnelはその隙間を埋めてくれる。
前提環境と準備物
手元の構成はこんな感じ。
| 項目 | 内容 |
|---|---|
| ハード | Raspberry Pi 5 (8GB) + Ubuntu Server 24.04 arm64 |
| 公開対象 | ローカルで動くFlaskアプリ(ポート8080) |
| ドメイン | Cloudflareにネームサーバーを委任済みの独自ドメイン |
| Cloudflareプラン | Free(Zero Trustも50ユーザーまで無料) |
独自ドメインは必須。Cloudflareにネームサーバーを向けていれば、サブドメインとしてトンネル先を割り当てる形になる。僕はもう何年もお名前.comで管理している独自ドメインを使い回していて、Cloudflareへのネームサーバー委任もダッシュボードからコピペで済んだ。![]()
cloudflaredのインストールと初期設定
ARM64版のバイナリをCloudflareの公式リポジトリから取得する。aptで入れるのが一番楽。
curl -L --output cloudflared.deb \
https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64.deb
sudo dpkg -i cloudflared.deb
cloudflared --version
次にCloudflareにログインして認証トークンを取る。
cloudflared tunnel login
ブラウザが立ち上がって(SSH越しの場合は表示されたURLを別端末で開く)、利用するドメインを選ぶとcertファイルが~/.cloudflared/cert.pemに保存される。ここで一度、cert.pemの保存場所を見失って30分溶かした。SSH先のホームディレクトリ直下に置かれるのだが、rootで実行していると/root/.cloudflared/に行ってしまうので注意。
トンネルを作る
cloudflared tunnel create techquant-home
# Created tunnel techquant-home with id: abcd1234-...
cloudflared tunnel list
作成すると~/.cloudflared/<UUID>.jsonが生成される。これが認証情報の実体。間違ってgitに上げないよう、gitignore必須。
config.ymlを書いてトンネルの向き先を定義する
ここが一番つまづきやすいパート。~/.cloudflared/config.ymlに以下のように書く。
tunnel: abcd1234-....
credentials-file: /home/pi/.cloudflared/abcd1234-....json
ingress:
- hostname: dash.example.com
service: http://localhost:8080
- hostname: api.example.com
service: http://localhost:5000
- service: http_status:404
ingressはリスト順に評価されるので、ワイルドカード的なhttp_status:404は必ず最後に置く。これを忘れると意図しないトラフィックを全部通してしまう。
続いてDNSレコードを自動で張る。
cloudflared tunnel route dns techquant-home dash.example.com
cloudflared tunnel route dns techquant-home api.example.com
Cloudflare側にCNAMEが自動生成され、オレンジ雲がオンになっている(=プロキシ経由)状態になる。
systemdで常駐させる
cloudflaredはフォアグラウンドでも動くが、常駐させないと再起動で止まる。
sudo cloudflared service install
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared
service installは~/.cloudflared/配下のconfigを/etc/cloudflared/にコピーする。コピー元が最新版でないと古い設定が残り続ける。ここは定期メンテで更新が要る部分。設定変更したら、
sudo cp ~/.cloudflared/config.yml /etc/cloudflared/config.yml
sudo systemctl restart cloudflared
を毎回やる。cronからsystemd timerへの移行記事で書いた通り、systemd運用に慣れておくとこういう常駐プロセスの扱いが楽になる。
ヘルスチェックをcronで回す
トンネルは稀に切れる。自宅回線が再接続された後、まれにcloudflaredが復旧しない現象を経験した。対策として5分ごとにヘルスチェックを回している。
*/5 * * * * curl -fsS https://dash.example.com/healthz \
|| systemctl restart cloudflared
乱暴だが、これで半年間ダウンタイムほぼゼロで運用できている。
Zero Trustでアクセスを絞る
デフォルトでは誰でもURLを叩けてしまう。管理用のダッシュボードを公開するなら、Google認証やメールOTPで絞りたい。Cloudflare Zero Trust(旧Access)の出番。
- Cloudflare Dashboard → Zero Trust → Access → Applications → Add an application
- Self-hostedを選び、
dash.example.comを入力 - Identity providerにGoogle(または内蔵のメールOTP)を追加
- Policy: 「特定のメールアドレスのみ許可」を設定
これで、dash.example.comに来るとCloudflareの認証画面が挟まり、許可したメールアドレスでGoogleログインしないと先に進めなくなる。社内ツール感覚で自宅サーバーを公開できるのは、正直ちょっと感動した。
注意:Zero TrustはFreeプランでも50ユーザーまで使えるが、機能の一部はPaidのみ。SSO連携など高度な用途なら有料プランを検討する。
ハマったポイント集
1. WebSocketが切れる
Flask-SocketIOを載せたアプリでWebSocket接続が頻繁に切れた。config.ymlの対象ingressにnoTLSVerify: trueではなく、タイムアウト設定を入れて解決。
ingress:
- hostname: dash.example.com
service: http://localhost:8080
originRequest:
connectTimeout: 30s
noHappyEyeballs: true
- service: http_status:404
2. Basic認証が二重で面倒
アプリ側で既にBasic認証をかけていると、Zero Trustと合わせて二重認証になる。Zero Trustに寄せるなら、アプリ側の認証は外すかサービスアカウント扱いにする。
3. トンネルが1日1回瞬断する
これは自宅回線(v6プラス)のIPv6アドレスが変わるタイミング。cloudflaredはv4で繋ぎ直せるので実害はないが、厳密にはログを見ると3-5秒のギャップがある。対策は諦めている。ここは正直まだ試行錯誤中。
セキュリティ面の考え方
ポート開放が不要という点で一段安全になるが、油断は禁物。
- Zero TrustのPolicyは必ず「allow specific email」形式にする。「everyone」にしない
- credentials-fileは必ず
chmod 600。漏れると誰でもそのドメインに偽サーバを立てられる - 公開するサービスは最小限に。ローカルの8080だけ通し、SSHやデータベースは絶対にingressに書かない
- Cloudflare側のログ(Analytics → Tunnels)を週1で見る習慣をつける
自動売買のような本番サービスを載せる場合は、さらに一段階踏み込んだ層を挟むのが無難。Raspberry Piで自動売買を運用するBookでも触れたが、金に関わる系統は公開しない運用が安全。
本格的な公開用途にはVPSも選択肢
自宅回線に載せ続けることにこだわりがなければ、最初からVPSで完結させる手もある。電気代と回線の安定性を考えると、月数百円のVPSに寄せたほうが結果的に安く上がることも。ちなみに自分は軽めのサービスはお名前.comの高性能VPSに載せ替えて、自宅のPi 5は開発&検証専用にしている。用途で分けるのが結局ラクだった。![]()
半年使った感想
Cloudflare Tunnelは、自宅サーバーを公開するときに考えることを大幅に減らしてくれた。ポート開放、DDNS、SSL証明書、IPアドレスの露出。これらの悩みが一気に消える。
一方、完全に透過というわけでもない。ingressの順序、config.ymlの配置場所、v6の瞬断、WebSocketのタイムアウト。小さな罠が点在していて、ちゃんと動かすには半日くらい向き合う必要がある。でも一度軌道に乗せれば、運用の手離れはかなり良い。
ポートを開ける気力はないが、自宅で動かしてるものを軽く外に見せたい。そのニーズにはこれ以上ない解だと思う。
本記事は筆者の自宅環境(Raspberry Pi 5 + Ubuntu 24.04)での検証・運用に基づく情報です。Cloudflareの仕様は変わる可能性があり、公開用途の運用は自己責任でお願いします。認証・アクセス制御は必ず適切に設定してください。