SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-27

Cloudflare TunnelでVPSのポートを閉じる設定手順

Cloudflare Tunnelを使ってVPSのポート開放を不要にする方法を解説します。cloudflaredのインストールから認証、ingressルール設定、systemdでの自動起動、そして80/443ポートを閉じてlocalhostへバインドするまでの手順を網羅しました。

Cloudflare Tunnel の役割と「ポート開放不要」の真意

Cloudflare Tunnel は、cloudflared と呼ばれる小さなデーモンを VPS 上で実行します。このデーモンは Cloudflare への外向き接続を確立し、それを維持します。ホスト名宛のリクエストは Cloudflare のエッジで受け付けられ、確立済みの接続を通じてサーバーへ転送されるため、サーバーに対して外部から直接接続を許可する必要はありません。

多くのガイドで触れられていない重要な手順があります。それは、トンネルをインストールしただけでは何も閉じられないという点です。ファイアウォールで 80 番ポートや 443 番ポートを開放したままにし、アプリが 0.0.0.0 で待ち受けを続けている場合、既存の経路を置き換えたのではなく、単に侵入経路を増やしたに過ぎません。オリジンサーバーの IP アドレスは依然として到達可能であり、それを特定した攻撃者は Cloudflare を経由せずに直接アクセスできます。これらのポートを閉じる作業は手動で行う必要があり、この手順こそが前述の対策を価値あるものにします。

cloudflared は、region1.v2.argotunnel.com および region2.v2.argotunnel.com の 7844 番ポートへの外向き通信を必要とします。QUIC プロトコルには UDP を使用し、HTTP/2 には TCP をフォールバックとして利用します。送信トラフィックを制限しているネットワーク環境では、両方のプロトコルを許可するか、--protocol http2 を使用して TCP 通信を強制してください。

開始前の準備

  • Cloudflare アカウントに登録済みで、Cloudflare のネームサーバーで管理されているドメインが必要です。cloudflared tunnel route dns はそのゾーンにレコードを書き込むため、事前にゾーンが存在している必要があります。
  • VPS からの 7844 番ポートへのアウトバウンド通信(UDP および TCP)が許可されている必要があります。
  • ローカルで待ち受け状態のアプリケーションが必要です。最初のテストであれば python3 -m http.server 8080 でも構いません。
  • サーバーに sudo がインストールされていること、およびファイアウォールの設定を変更する前に、別の SSH セッションを開いておく必要があります。

Ubuntu または Debian への cloudflared のインストール

Cloudflare はすべての .deb リリースに cloudflared パッケージを添付しているため、インストールは 1 回のダウンロードと 1 回の dpkg 呼び出しで完了します。

curl -fsSL -o /tmp/cloudflared.deb \
  https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i /tmp/cloudflared.deb
cloudflared --version

サーバーのアーキテクチャが不明な場合は、最初に dpkg --print-architecture を実行してください。64 ビット ARM の場合、ファイル名の末尾は amd64 ではなく arm64 になりますが、それ以外の手順に変更はありません。cloudflared --version でバージョン文字列が表示されれば、次のステップへ進むための確認は完了です。

この方法でインストールされたパッケージは apt の更新パスに含まれないため、apt-get upgrade によって更新されることはなく、更新作業は管理者の責任となります。sudo cloudflared update を実行すると最新のリリースが取得され、バイナリが上書きされます。サービスが既に存在する場合は、続けて sudo systemctl restart cloudflared を実行し、実行中のプロセスを新しいバイナリに置き換えてください。トンネルデーモンはポートを開放しないもののインターネットに面したソフトウェアであるため、他のパッチ適用と同様のスケジュールで更新を行ってください。

ログインと名前付きトンネルの作成

cloudflared tunnel login

ヘッドレスの VPS ではブラウザが起動しないため、表示された URL をコピーして手元のラップトップのブラウザに貼り付け、ゾーンを選択してください。完了すると ~/.cloudflared/cert.pem が作成されます。

cert.pem はお客様の アカウント 認証情報です。これにはトンネルの作成、対象ゾーンへの DNS レコードの書き込み、トンネルの削除を行う権限が含まれます。実行中のトンネルがこのファイルを使用することはありません。このファイルが 1 つあればドメイン上で新しいホスト名を公開できてしまうため、パスワードと同様に厳重に管理してください。

cloudflared tunnel create homelab

正常に実行されると以下の 2 行が表示されます。この中の UUID は、後ほど設定ファイルに貼り付ける値となります。

Tunnel credentials written to /home/you/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.
Created tunnel homelab with id 6ff42ae2-765d-4adf-8112-31c55c1551ef

この JSON ファイルはトンネルの識別情報であり、実行中のサービスが必要とする唯一の認証情報です。このファイルを保持している者は誰でもトンネルとして登録し、トラフィックを受信できてしまいます。このファイル単体をローテーションする方法はありません。無効化するには cloudflared tunnel delete homelab を実行し、新しいトンネルを作成する必要があります。

認証情報ファイルを適切な場所に配置する

このサービスは root 権限で実行されます。そのため、バックアップジョブや共有ログインからアクセスされる可能性のあるホームディレクトリではなく、root が所有するディレクトリにファイルを配置してください。

sudo install -d -m 700 -o root -g root /etc/cloudflared
sudo install -m 600 -o root -g root \
  ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json \
  /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
rm ~/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
sudo ls -l /etc/cloudflared
cloudflared tunnel list

ls -l を実行すると、JSON ファイルに対して -rw------- root root が表示されるはずです。cloudflared tunnel listcert.pem を読み込むため、引き続き動作します。また、トンネル名、UUID、および現在の接続数が表示されます。

config.yml に実際のイングレスルールを記述する

設定ファイルはホームディレクトリではなく、/etc/cloudflared/config.yml に作成してください。理由は以下の通りです。cloudflared service install は見つかった設定ファイルを /etc/cloudflared/config.yml にコピーし、その内容を systemd ユニット内に --config /etc/cloudflared/config.yml としてハードコードします。~/.cloudflared/config.yml でファイルを作成すると、そのコピーは一度限りのスナップショットとなります。その後ホームディレクトリ側のファイルを編集しても反映されず、サービスは古いルールのまま動作し続け、警告も表示されません。ファイルを直接配置することで、この問題は完全に回避されます。

tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /etc/cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
loglevel: info

ingress:
  - hostname: app.example.com
    service: http://127.0.0.1:8080
  - hostname: files.example.com
    service: http://127.0.0.1:8081
  - hostname: grafana.example.com
    path: ^/api/
    service: http://127.0.0.1:3000
  - service: http_status:404

ルールは上から順に評価され、最初に一致したものが適用されます。hostname を指定しないルールはすべてのホスト名に一致するため、キャッチオール(デフォルト)ルールは必ず最後に配置する必要があります。これを省略すると、設定は The last ingress rule must match all URLs (i.e. it should not have a hostname or path filter) エラーで拒否されます。http_status:404 は 404 を返すだけの組み込みサービスです。これには重要な役割があります。これがないと、公開するつもりのなかったホスト名へのリクエストが、たまたま最後に記述されたルールへ転送されてしまうためです。

URL の service: には localhost ではなく 127.0.0.1 を使用してください。Ubuntu では localhost が優先的に ::1 へ解決されますが、IPv4 ループバックのみで待機しているアプリは、その接続を拒否します。その際のログは dial tcp [::1]:8080: connect: connection refused となり、訪問者には 502 エラーが表示されます。

通信がマシン内から出ないため、ここでは単純な http:// が適切です。ローカルアプリが TLS (transport layer security) を必須とする場合にのみ https:// を使用してください。その際、証明書の名前が一致しないと x509: certificate is valid for example.com, not localhost が発生します。これを修正するには originRequest 配下に originServerName を設定するか、リスクを許容して noTLSVerify: true を使用してください。

サービスを開始する前に、ルールを確認します。

sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress validate
sudo cloudflared --config /etc/cloudflared/config.yml tunnel ingress rule https://app.example.com/login

ingress validate は設定が有効かどうかを報告し、無効な場合は該当するルールを指摘します。ingress rule は URL を 1 つ受け取り、それに一致する最初のルールを表示します。これは path の正規表現が想定通りに一致していないことを確認する最も迅速な方法です。

トンネルへ DNS を向ける

cloudflared tunnel route dns homelab app.example.com
cloudflared tunnel route dns homelab files.example.com
cloudflared tunnel route dns homelab grafana.example.com

各呼び出しにより、6ff42ae2-765d-4adf-8112-31c55c1551ef.cfargotunnel.com を指すプロキシ済みの CNAME レコードが書き込まれます。このターゲットは Cloudflare のネットワーク内部でのみ解決されるため、ホスト名のパブリック DNS 応答には Cloudflare のアドレスが含まれ、VPS の IP アドレスは一切公開されません。config.yml 内のワイルドカード hostname を使用する場合でも、実際に使用する名前ごとに対応する DNS レコードが必要です。

レコードが既に存在する場合、コマンドは以下のように失敗します。

Failed to add route: code: 1003, reason: Failed to create record app.example.com with err An A, AAAA, or CNAME record with that host already exists.

その既存のレコードは、ほとんどの場合、VPS のパブリック IP を指している古い A レコードです。これこそが削除すべきレコードです。Cloudflare ダッシュボードでそのレコードを削除してから、再度コマンドを実行してください。レコードを残したままにすると、DNS がオリジン IP を公開し続けるため、トンネルによる隠蔽効果が失われます。

再起動後も動作するようサービスとしてインストールする

まずはフォアグラウンドで一度実行してください。端末上で直接確認する方が、journal でログを追うよりもエラーを把握しやすいためです。

sudo cloudflared --config /etc/cloudflared/config.yml tunnel run homelab

正常に起動すると、エッジロケーションごとに Registered tunnel connection 行がログ出力され、それぞれに connIndex が割り当てられます。ブラウザでホスト名にアクセスし、Ingress ルールに従って正しく転送されることを確認したら、Ctrl-C で停止します。

sudo cloudflared --config /etc/cloudflared/config.yml service install
systemctl status cloudflared

このコマンドにより /etc/systemd/system/cloudflared.servicecloudflared-update.service および cloudflared-update.timer とともに書き出され、続いて systemctl enable cloudflared.servicesystemctl start cloudflared.service が実行されます。ここで重要なのは enable です。これが再起動後にトンネルを復旧させる役割を担うためです。ユニットの ExecStartcloudflared --no-autoupdate --config /etc/cloudflared/config.yml tunnel run に固定されているため、設定ファイルのパスは変更できません。

この手順で発生する3つのエラーには、それぞれ明確なメッセージがあります。possible conflicting configuration in /home/you/.cloudflared/config.yml and /etc/cloudflared/config.yml は両方のファイルが存在しており、cloudflared がどちらを使うか判断できない状態です。不要な方を削除してください。configuration file ... must contain entries for the tunnel to run and its associated credentials (tunnel: TUNNEL-UUID, credentials-file: CREDENTIALS-FILE) は、設定で名前付きトンネルのキーではなく簡易的な url: を使用していることを示します。この形式ではサービスとして実行できません。cloudflared service is already installed は古いユニットが残っている状態です。先に sudo cloudflared service uninstall を実行してください。

リロード機能はありません。/etc/cloudflared/config.yml を編集した後は、sudo systemctl restart cloudflared を実行します。その後、再起動の動作を推測ではなく実機で確認します。

sudo reboot
# once it is back
systemctl is-enabled cloudflared
systemctl is-active cloudflared
journalctl -u cloudflared -n 50 --no-pager
curl -sI https://app.example.com

is-enabledenabled を出力し、is-activeactive を出力することが、このセクションの目的です。ルートが確立されサービスが稼働していれば、サーバー上で cert.pem を実行し続ける必要はありません。rm ~/.cloudflared/cert.pem。後からホスト名を追加する場合は、再度 cloudflared tunnel login を実行するだけで済みます。

80番および443番ポートを閉じる、さもなくばトンネルは単なる追加経路に過ぎない

2つの変更が必要です。両方実施しなければ、オリジンサーバーは外部から到達可能なままです。

まず、アプリケーションをループバックアドレスにバインドします。Nginxの場合、nginxリバースプロキシ設定解説で説明した通り、listen 80;の代わりにlisten 127.0.0.1:8080;を指定します。Docker Composeの場合はports: - "127.0.0.1:8080:80"とします。単なる"8080:80"という形式はすべてのインターフェースで公開されてしまい、Dockerが独自のNAT(ネットワークアドレス変換)ルールを書き込むため、ufwがパケットを検知する前に通信が通過してしまいます。ufwの拒否ルールが機能しないこの罠については、Dockerの公開ポートがufwを無視する理由を参照してください。

sudo ss -lntp

移行したすべてのサービスは、Local Address列で127.0.0.1:8080と表示されるはずです。0.0.0.0:8080*:8080と表示されている行は、依然として外部からの接続を待ち受けています。

次に、ポートを閉じます。

sudo ufw status numbered
sudo ufw delete allow 'Nginx Full'
sudo ufw status

拒否ルールを重ねるのではなく、80番および443番の許可ルールを削除してください。ufwは最初に一致したルールで処理を停止するため、リストの上位に残った古い許可ルールが優先されてしまうからです。SSHのルールは維持してください。ufwファイアウォール基本ガイドで残りのルールセットを解説しています。多くのVPSプロバイダーはコントロールパネル上で個別のネットワークファイアウォールを提供しており、これはufwとは別物であるため、そこでも80番および443番を閉じてください。

次に、外部から検証を行ってください。サーバー自身でcurl http://127.0.0.1:8080を実行しても、外部からの到達可能性は証明できないためです。

# run these from a different machine
nc -vz 203.0.113.10 443
curl -sI https://app.example.com

生のIPアドレスに対してncで接続拒否またはタイムアウトが発生し、ホスト名経由で200が成功することが、目指すべき結果です。ポートが本当に開いているかの確認方法で、より詳細なテスト手法を解説しています。

トンネルはあくまで転送手段であり、認証機能ではありません。トンネル経由で公開するものはすべて、ログイン認証を前段に配置しない限り誰でもアクセス可能です。エッジ側でCloudflare Accessを利用するか、アプリの前段にOAuth2プロキシを配置してください。SSHについても個別の対策が必要です。トンネルはSSHを保護しないため、22番ポートは開いたままにしつつ、接続元IPアドレスを制限してください。

Cloudflare Tunnel の利点とコスト

得られるメリットは実用的です。オリジン IP は公開されず、インバウンドポートも開放されません。パブリック IP を持たないマシンからでもセットアップが可能であり、公開証明書は Cloudflare 側で管理されるため、サーバー上で ACME (automatic certificate management environment) クライアントを動かす必要もありません。また、ボリューム型攻撃は帯域幅を消費される前にエッジで吸収されます。

コストも同様に実用的です。Cloudflare はエッジで TLS 終端を行います。訪問者のリクエストはそこで復号され、トンネル内で再暗号化されるため、Cloudflare はトラフィックを読み取ることが可能です。これこそがファイアウォール、キャッシュ、Access ルールを実現する仕組みであり、プロキシを利用する限りこれを無効化する設定はありません。プレーンテキストが第三者に渡ることを許容できない場合は、ここで利用を中止し、別の手段を選択してください。

また、Cloudflare は到達性における強力な依存先となります。cloudflared が接続されていない場合、訪問者はアプリの代わりに Cloudflare の Error 1033 ページを表示することになります。直接的なルートを意図的に排除しているため、フォールバック先も存在しません。

一般的なブラウザからパブリックホスト名に到達できるのは HTTP、HTTPS、WebSocket のみです。SSH、RDP (remote desktop protocol)、ゲームサーバーなど、その他の TCP プロトコルを利用するには、クライアント側にもソフトウェアが必要です。cloudflared access tcp を使用してローカルポートを転送するか、WARP クライアントを使用する必要があります。これらにはクライアント不要の経路は存在しません。

リクエストボディはエッジで制限されており、上限を超えるアップロードはアプリに到達する前に HTTP 413 で拒否されます。2026 年 8 月時点で、この上限は Free プランおよび Pro プランで 100 MB であり、上位プランではさらに高くなります。数値を前提とした設計を行う前に、Cloudflare の最新の制限ページを確認してください。また、Cloudflare のセルフサービス利用規約では、プロキシを主に動画やその他の巨大な非 HTML ファイルの配信に使用することを制限しています。メディアライブラリを無料のトンネルに向ける前に、規約を確認しておくことを推奨します。

Cloudflare Tunnel、リバースSSHトンネル、Tailscale Funnel

これら3つはすべてアウトバウンド専用であるため、インバウンドポートやパブリックIPを持たないサーバーからでも利用可能です。違いは、誰が平文を保持するか、そして公開時にどのホスト名を使用するかという点にあります。

リバースSSHトンネルには、パブリックIPを持つ2台目のマシンが必要です。そのマシンが玄関口となり、証明書、リバースプロキシ、ファイアウォールの運用はすべて自分で行います。他者がデータを復号することはありません。管理対象が増えるため、ネットワークの一時的な切断に対応するには autossh や Restart=always を設定した systemd ユニットが必要です。CGNAT環境でのリバースSSHトンネル構築ガイドでその手順を解説しています。

Tailscale Funnel は最も近い比較対象です。同様にアウトバウンド専用であり、TLS終端は自身のマシンで行われるため、Tailscaleの中継サーバーが平文を見ることはありません。制約は名前とポートです。Funnelは自身のtailnetの ts.net ドメイン配下の名前のみを扱い、ポートは 443、8443、10000 に限定されます。Tailscale ServeとFunnelの違いで両者の詳細を説明しています。

自身の制約条件に合わせて選択してください。公開URLに独自のドメインを使用する必要があり、Cloudflareによるトラフィックの読み取りを許容できる場合は Cloudflare Tunnel を選択します。ts.net ホスト名で問題なく、プロキシに平文を渡したくない場合は Tailscale Funnel を選択します。すでにパブリックなサーバーを所有しており、通信経路に第三者を一切介在させたくない場合はリバースSSHトンネルを選択してください。

FAQ

Cloudflare Tunnel を使用する場合でも 443 番ポートを開放する必要がありますか?

いいえ。cloudflared は 7844 番ポートを使用して Cloudflare へ外向きに接続し、すべてのリクエストはその接続経由で戻るため、インバウンドポートは使用されません。ただし、トンネルをインストールしても既存のポートが自動的に閉じるわけではありません。ufw で 80 番および 443 番ポートの許可ルールを削除し、プロバイダー側のネットワークファイアウォールでもそれらを閉じ、アプリを 127.0.0.1 にバインドし、VPS の IP を公開している残りの A レコードを削除してください。サーバー上で sudo ss -lntp を、別のマシンから nc -vz <your-ip> 443 を実行して確認します。

ホスト名で Cloudflare Error 1033 が表示されるのはなぜですか?

Error 1033 は、Cloudflare がそのホスト名の DNS レコードを保持しているものの、リクエストを受け取るための正常な cloudflared を見つけられないことを意味します。プロセスが停止しているか、起動していても Cloudflare に到達できていません。systemctl status cloudflaredjournalctl -u cloudflared -n 50 を確認し、UDP と TCP の両方で 7844 番ポートの外向き通信が許可されていることを確認してください。UDP と QUIC をブロックし、TCP フォールバックを許可しないファイアウォール設定ではこのエラーが発生します。cloudflared tunnel info homelab で Cloudflare が現在認識している接続を確認でき、リストが空であれば原因はサーバー側にあります。

トンネル経由で 502 Bad Gateway が発生するのはなぜですか?

502 は cloudflared には到達できたものの、ローカルサービスに到達できなかったことを意味します。そのため、問題は Cloudflare ではなく、その中間にあります。ログを確認してください。dial tcp [::1]:8080: connect: connection refused は指定した場所にリッスンしているプロセスがないことを示します。そのメッセージ内の [::1] は、アプリが IPv4 のみをバインドしているにもかかわらず、service: URL に localhost と記述していることが原因である場合が多いため、代わりに http://127.0.0.1:8080 と記述してください。HTTP/1.x transport connection broken: malformed HTTP response はその逆の不一致であり、プレーンな HTTP で通信するオリジンに対して https:// と記述した場合に発生します。

Cloudflare Tunnel 経由で SSH、RDP、またはゲームサーバーを実行できますか?

通常のクライアントからは実行できません。トンネル経由のパブリックホスト名は HTTP、HTTPS、WebSocket を伝送しますが、これらはブラウザが使用するプロトコルです。その他の TCP プロトコルには、クライアントマシン側にも cloudflared access tcp によるローカルポート転送や WARP クライアントなどのソフトウェアが必要です。何もインストールせずに任意の環境から SSH を利用したい場合、トンネルは適していません。22 番ポートを開放し、接続元 IP アドレスで制限してください。

Cloudflare はトンネル経由のトラフィックを閲覧できますか?

はい。Cloudflare はエッジで TLS 終端を行い、そこでリクエストを復号してから、トンネルを通じてサーバーへ再暗号化して送信します。この復号処理によってファイアウォール、キャッシュ、Access ポリシーが機能しますが、同時にプレーンテキストが Cloudflare のマシン上に存在することにもなります。同社のプロキシを使用する限り、これを回避する設定はありません。それが許容できない場合は、Tailscale Funnel を使用するか、公開サーバー上で独自のリバースプロキシを運用してください。

#cloudflare-tunnel#cloudflared#zero-trust#firewall#self-hosting