Chillarin Blog 構成紹介 - 2.公開の核:Cloudflare Tunnel設計 -
ポート開放なしで自宅ブログを安全に公開するための Cloudflare Tunnel 設計を解説。cloudflared / Docker / ingress の構成思想と、DNS・証明書まわりで詰まりやすい箇所をハマりポイント中心にまとめる。
本章の内容
前回(1.物理・仮想基盤編)で、NUC×3 + Proxmox の土台を整理しました。 今回はその上に乗る「外部公開の核」= Cloudflare Tunnel の設計・思想・詰まりポイントをまとめます。
- ポート開放なしで、自宅ブログを安全に公開する思想
- Tunnelの構成(Docker / cloudflared / ingress の考え方)
- DNS/証明書周りで詰まりやすい箇所だけ濃く(ハマりやすいポイント)
公開URL(今回の構成)
- blog:
https://chillablog.chillarin39.com/ - comments:
https://comments.chillarin39.com/
全体フローイメージ
公開URLを実現するためのイメージ図は以下となります。

以降の説明は、裏側で常時動く「トンネル確立」と、アクセスのたびに起きる「ユーザアクセス」に分けて整理します。
(A) トンネル確立(裏側・常時)
- cloudflared(自宅)→
region1/region2.v2.argotunnel.com:7844(Cloudflare)へ outbound 接続(QUIC/HTTP2) - 認証してトンネルセッション確立・維持(切れたら自動で再接続)
ポイント:
- 家庭側で ポート開放は不要(全部 outbound で始まる)
- cloudflared は 常時接続でトンネルを張り続ける(切れたら再接続する)
(B) ユーザアクセス(表側・都度)
- Client → DNS で
chillablog...を解決 - DNS 応答:Cloudflare Edge の Anycast IP(IPv4/IPv6 が複数返ることもある)
- Client → Cloudflare Edge へ
https://chillablog...:443でアクセス - Cloudflare Edge →(確立済みトンネル)→ cloudflared
- cloudflared →
http://web:80(nginx)へ転送(commentsはhttp://remark42:8080)
Anycast IP について: Anycast は「同じIPアドレスを世界中の拠点が広告し、クライアントは"最寄り"の拠点へ到達する」仕組みです。 “グローバルIP” と同義ではなく、ここでは 「Cloudflareのエッジに最寄りで到達させるためのIP(=Anycast)」 という意図を明確にするために Anycast と書いています。
以降、実際の設定方法や動作確認のポイントを記載していきます。
Cloudflare設定
Cloudflareと自宅サーバがトンネル形成をするまでに必要な内容を、それぞれの観点(Cloudflare側 / 自宅側)から整理します。
Cloudflareの設定(GUI)
Cloudflare側の設定を以下にまとめておきます。 (アカウント作成・ドメイン追加などは割愛します)
1) Zero Trust で Tunnel を作成(トンネルID/認証情報の発行)





「次へ」をクリックすると Public Application Route(公開されたアプリケーションルート)の登録画面へ遷移します。
ちなみに、トークンは "eyJhIjoiMD..." から始まる文字列です。
メモ:この時点では「外部公開URLの設定」ではなく、あくまで トンネル本体(器) を作っている段階です。
2) Public Application Route(公開されたアプリケーションルート)を登録(Host名 → Service の対応付け)
公開URL(Host名)と、自宅側で動いているサービス(転送先)を紐づけます。
chillablog.chillarin39.com→http://web:80comments.chillarin39.com→http://remark42:8080

サブドメイン
- 公開するFQDNの「左側(ホスト名)」です。
- 例:
chillablogを入れると、chillablog.chillarin39.comになります。
ドメイン
- 公開するFQDNの「右側(ゾーン)」です。Cloudflareに登録・管理しているドメインから選びます。
- 例:
chillarin39.comを選ぶと、その配下にサブドメインをぶら下げられます。 - ※この設定を作ると、Cloudflare側で該当FQDNのDNSレコード(トンネル向け)が自動作成されます。
パス(オプション)
- URLのパス部分での条件(ルーティング条件)です。未指定なら「そのホスト名への全リクエスト」が対象になります。
- 例:
/aiを入れると、https://chillablog.chillarin39.com/aiのように特定パスだけをこのルールで転送できます。 - ※パスで分岐させたい時に使います(ホスト名で分けるなら空でOK)。
タイプ
- cloudflared が「転送先サービス」へ接続する際のプロトコルです。
- 例:
HTTPを選ぶと、トンネル出口(cloudflared)→転送先(web/remark42)間はHTTPでつなぎます。 - (ここは「ユーザ → Cloudflare」がHTTPSかどうかとは別の話で、“自宅側(出口以降)” の接続方式です)
URL
- cloudflared から見た「転送先(オリジン)」の宛先です(スキーム + ホスト名:ポート)。
- Docker構成だと、同一 docker network 内でサービス名が名前解決できるので
http://web:80のように書けます。 - 例:
- blog →
http://web:80(nginx) - comments →
http://remark42:8080(Remark42)
- blog →
ここで設定する「Service / URL」は Cloudflareがインターネット越しに直接叩きに行く宛先ではなく、トンネル出口(cloudflared)から見た転送先です。
つまり、上の web や remark42 は docker-compose の service 名(同一ネットワーク内の名前解決)になっています。
3) 振り分け方式(Host名で blog / comments を分ける理由)
今回、ブログ(Hugoの生成物を配信)とコメント機能(Remark42)は 別Host名で動作させています。
- blog:
chillablog.chillarin39.com→http://web:80 - comments:
comments.chillarin39.com→http://remark42:8080
ポイントとしては、実体(サーバ/VM)が1台でも Host名で分ける意味は大きいことです。 Host名で分離する目的を一言でいうと 運用性と安全性の向上です。
- Host名で分けるメリット
- 役割がブレない
- blog は 静的コンテンツ配信(HTML/CSS/画像などをそのまま配る)
- comments は 動的なWebアプリ(投稿・取得でサーバ処理/データ更新が走る)
- ブラウザ周り(Cookie / iframe / SameSite / CORS)で沼りにくい
- 特にコメントのような"書き込み"が絡むものは、分離しておくと後から楽
- Cloudflare側の制御をホスト単位で整理できる(最適化・制限・例外など)
- 障害時の切り分け・影響範囲が小さい(片方だけ止める/差し替えるがやりやすい)
- ルーティングが単純で、未来の自分が読み返しやすい
- 役割がブレない
4) DNS(レコード)と SSL/TLS

- DNSレコード概要:
- Cloudflare 管理の DNS に
chillablog/commentsを登録 ※Public Application Route の設定時に自動作成される挙動になります - DNSレコードの内部表現としては、一般的に
<tunnel-id>.cfargotunnel.comを向き先にした CNAME になります (<tunnel-id>は「どのトンネルに流すか」を特定するための識別子) - ただしクライアント側から見ると、DNS応答では Cloudflare Edge の IP(Anycast)が返ります (Cloudflareのプロキシ配下なので、自宅のIPはDNSに出ない)
- Cloudflare 管理の DNS に

- SSL/TLS:
- SSL/TLS 暗号化モードは Full
- 理由:Flexible は「HTTPSで来た通信を、途中でHTTPに落とす」系の事故(混在・リダイレクトループ・Cookie周り)を起こしやすいので避ける。 Tunnel 自体は暗号化されますが、サイト全体の前提として Full 以上に寄せておくと運用事故が減ります。
- Origin cert は未使用(今回の構成)
- 理由:Tunnel は cloudflared が Cloudflare へ outbound で接続してくれるため、いわゆる「オリジン(nginx)に証明書を入れて Cloudflare が検証する」構成にしなくても成立します。 まずは構成をシンプルにし、必要になったら(内部もHTTPSにしたい等)Origin cert を検討する方針です。
- SSL/TLS 暗号化モードは Full
5) 詰まりポイント
- UI変更が多いため、AIなどに聞きながら作業すると手順やメニュー名がズレて迷いやすい
- Public Hostname の Host / Service(URL)の書き方が合ってるか判断しづらい
- iframe で go2rtc を通すときは path 許可が増えがち → 開発者ツール(Network / Console)を見ながら許可パスを足していく のが現実的 ※別途 go2rtc の説明記事で詳細を書く予定
自宅サーバ(cloudflared)の設定(Docker)
cloudflared は Cloudflare Tunnel を張るためのクライアントです。 「仮想マシンそのもの」ではなく、Cloudflareへ outbound で接続してトンネルを維持するプロセス/コンテナという理解です。
- 形態:Docker(cloudflared コンテナ)
- 稼働ホスト:
chllarin-sv01 - 関連ディレクトリ:
/opt/chirarin-blog-infra/
docker-compose(抜粋:web / remark42 / tunnel)
📦 この設定ファイルの全量は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
構成のポイントを補足します。
3つのサービスを1つの docker-compose で管理している
- web (nginx): Hugo の生成物を配信。Cloudflare Tunnel からは
http://web:80で到達する - remark42: コメント機能。Cloudflare Tunnel からは
http://remark42:8080で到達する - tunnel (cloudflared): Cloudflare Tunnel を張るクライアント。web と remark42 に
depends_onで依存している
全サービスが同一の Docker network(bridge)に所属している
これにより、cloudflared からは web / remark42 というサービス名で名前解決できます。Cloudflare の Public Application Route で設定した URL(http://web:80 / http://remark42:8080)がそのまま機能する仕組みです。
TUNNEL_TOKEN は環境変数で注入している
Cloudflare 側で作成した Tunnel に紐づく認証用トークンです。自宅側の cloudflared はこのトークンで Cloudflare に対して「自分はこの Tunnel の正当なクライアントです」と証明します。この値は GitHub に直接置かず、.env や docker compose --env-file、あるいはホスト側の環境変数で注入するのが安全です。
📦
.envファイルの設定例は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
実通信確認
Cloudflare - cloudflared 通信確認
Cloudflare Edge まで通信がされ、Tunnel が張れているかを確認します。 (観点:DNS → 実際の UDP/7844 → NAT → cloudflared 側ログ)
1) まずは DNS 名前解決できるか
📦 この実行結果(dig コマンドの出力)は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
region1/region2.v2.argotunnel.comが 複数IPを返すのは正常です(Cloudflare Edge は多拠点運用)。- 「DNSに10個出てる=10本のトンネルを全部使う」ではありません。
実際に cloudflared が同時に張る本数の目安は、ログの
connIndex=0..3のように 概ね4本です(後述)。 - DNS 側は「候補の Edge IP を多めに返す」→ cloudflared 側は「その中から実際に繋がる相手へ複数本張る」という役割分担です。
2) Server → Cloudflare Edge への通信(UDP/7844 を見る)
📦 この実行結果(tcpdump コマンドの出力)は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
port 7844/UDPで外向き通信が見える=cloudflared が Cloudflare Edge に outbound 接続できています。- 宛先IPが複数出ている= 冗長用に複数セッション(複数コネクション)を張っていることを示します(後述の connIndex と対応しやすいです)。
3) Cisco 1111 側で NAT 確認(アウトバウンドしている証拠を取る)
📦 この実行結果(show ip nat translations の出力)は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
Outside globalが198.41.xxx.xxx:7844になっている=Cloudflare Edge 宛に NAT 越しで通信していることが分かります。Inside localのポート番号が tcpdump の送信元ポートと対応します。 「サーバのパケット」⇔「ルータのNATテーブル」が繋がるので、運用メモとして強い証拠になります。Inside globalは「NAT後の外側(WAN側)に見える送信元IP」です。 もし ISP の事情で WAN 側がプライベートIP(CGNAT配下など)になっている場合、ここに グローバルIPではない値が出ることもあります(今回の環境がまさにそれです)。
4) cloudflared のログ(Tunnel確立の状態確認)
📦 この実行結果(docker logs の出力)は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
Registered tunnel connectionが出たら、Cloudflare 側にトンネル接続が登録(確立)できた合図です。connIndex=0..3が並ぶ=同時に複数(この例では4本)のコネクションを張って冗長化している状態です。 DNSで見えた「候補IPが大量」でも、実際に確立しているのはこのRegistered...行で見える本数が目安になります。protocol=quicなので、あなたの環境では QUIC(UDP/7844)で張れていることが tcpdump と一致します。location=nrt05/nrt07/nrt09は **接続先の Cloudflare Edge 側の拠点(PoP)**を示します。 同じ東京圏でも複数の location に分散しているのが見えて、これも冗長性の根拠になります。
5) Cloudflare Web GUI からの確認
CloudflareのWeb GUI でもステータスが正常になっていることを確認できます。

コネクタIDをクリックすると、診断画面も表示可能です。

ポイント
- GUI は「今つながっているか」「どのコネクタ(cloudflared)か」を見る用途には便利ですが、パケットレベルの詳細までは追いにくいです(詳細は tcpdump / NAT / cloudflaredログが強い)。
- 画面上の「Origin IP」は、Cloudflareから見た **cloudflared の接続元IP(回線側に見える送信元)**です。 CGNAT配下などの環境では「自宅専用のグローバルIP」ではなく 共有の出口IPとして見えることがあります。
- 画面上の「Private IP」は、cloudflared が動いている ローカル側のIPとして表示されます。 Dockerの場合、ホストOSのIPではなく **コンテナのIP(例:172.18.x.x)**として見えることがあります。
- 重要なのは「外部から自宅への inbound を開けずに公開できている」点です。 一方で、cloudflared の outbound 接続元(回線側に見えるIP)は Cloudflare からは見えます(これは仕様です)。
Client - Chillarin Blog 通信確認
一般のClientから Chillarin Blog への通信を確認します。
1) まずは DNS 名前解決できるか
📦 この実行結果(nslookup コマンドの出力)は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
chillablog.chillarin39.comは Cloudflare のプロキシ配下なので、Cloudflare Edge のIP(Anycast)が返ります。- 一方で
chillarin39.com(ゾーンのトップ/apex)は、設定によっては A/AAAA を持たないことがあります。 ※「トップはIPが返らないが、サブドメインは返る」は運用として普通にあり得ます。 - IPが複数返るのは正常で、IPの数=PoPの数ではありません。実際にどのPoPに到達するかは Anycast で決まります。
2) https://chillablog.chillarin39.com にアクセスした際の動作
- ユーザが Webブラウザから
https://chillablog.chillarin39.comにアクセス(HTTPS/443) - DNS で返ってきた Anycast IP に到達し、Cloudflare Edge(最寄りPoP)が HTTPS を受信
- Edge は、すでに確立済みの Cloudflare Tunnel(複数本のうちのどれか)へリクエストを流す
- 自宅側の
cloudflaredが受信し、ingress ルールに従って内部サービスへ転送する- blog:
http://web:80(nginx。Hugo生成物を配信) - comments:
https://comments.chillarin39.com→(Tunnel経由で)http://remark42:8080(Remark42)
- blog:
📦 nginx のアクセスログの実行結果は 有料コンテンツ: 自宅サーバ構築シリーズ に含まれています。
ポイント
「クライアント通信が web(nginx)まで届いている証拠」
- nginx のアクセスログが出ている= Cloudflare Edge →(確立済みトンネル)→ cloudflared →
http://web:80まで転送され、nginx が実際に処理したということです。
- nginx のアクセスログが出ている= Cloudflare Edge →(確立済みトンネル)→ cloudflared →
Docker ネットワーク内の送信元IP が表示される
- nginx から見た送信元IPが
172.18.0.xのようなDockerネットワークのIPになるのは、リクエストが cloudflared コンテナ経由で来ているためです。 - つまり「インターネットから直接 nginx に来てる」のではなく、意図通り トンネル経由になっています。
- nginx から見た送信元IPが
X-Forwarded-For で “実クライアントIP” を追える(設定次第)
- nginx の
log_formatで$http_x_forwarded_forを出している場合、末尾に元のクライアントIPが記録されます。 - 「誰が見に来たか」を追うならここが重要です。
- nginx の
ステータス
304は「キャッシュが効いてる」304 Not Modifiedはブラウザがキャッシュを持っていて、「更新ないなら手元のキャッシュ使うね」という応答です。- 疎通確認としては 200でも304でもOK(届いて処理されていることが重要)。
ここまでのまとめ
- cloudflared は **Cloudflare Tunnel を張るクライアント(プロセス/コンテナ)**で、常時トンネルを維持する
- cloudflared は outbound で
region*.v2.argotunnel.com:7844に接続し、QUIC(UDP/7844)または HTTP2 でトンネルを確立する - 公開側は Cloudflare Edge(Anycast) が受け、確立済みトンネル経由で自宅の cloudflared に転送される
- 今回は Host名で分離し、役割の異なる2つのサービスをシンプルに運用している
- blog(静的配信)→
http://web:80(nginx) - comments(動的Webアプリ)→
http://remark42:8080(Remark42)
- blog(静的配信)→
- 疎通確認は「DNS(候補)→ UDP/7844(実通信)→ NAT(証拠)→ cloudflaredログ(確立)→ nginxログ(ユーザ到達)」の順で追うと強い
次回は、 Hugoでブログを作る(テーマ・テンプレの基本) について解説していきます。
自宅サーバ運用の全体像は 自宅サーバ運用の完全ガイド — Proxmox + Cloudflare Tunnel + Docker で個人ブログを公開し続ける にまとめています。Proxmox クラスタ・公開経路・Docker 本番化・障害対応までを 1 ページで通読できる Pillar ガイドです。
コメント