Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】WebRTC works on your laptop. Here is what breaks when you put it on a server.

2026年09月19日に「Dev.to」が公開したITニュース「WebRTC works on your laptop. Here is what breaks when you put it on a server.」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

WebRTCをサーバーにデプロイすると、ローカルで動いても問題が生じやすい。シグナリングとメディアで通信経路が異なり、サーバーが誤ったIPを広告したり、クラウドのファイアウォールやリバースプロキシが通信を遮断する。メディア通信経路の正しい設定と確認が重要だ。

ITニュース解説

WebRTC(Web Real-Time Communication)は、ウェブブラウザ間で直接リアルタイムの音声や映像をやり取りするための技術だ。開発用PC上で動いていたWebRTCアプリケーションを、いざインターネット上のサーバー(特にクラウド仮想マシン)にデプロイしようとすると、思いがけない落とし穴に遭遇することがよくある。特に「接続は確立しているのに、なぜか映像が表示されない」という状況は、多くの開発者を悩ませる典型的なパターンだ。この現象の背後には、複数の異なる原因が潜んでおり、それぞれ異なるアプローチでの解決が必要になる。

WebRTCの通信は、大きく分けて「シグナリング」と「メディア」という二つの異なる経路で成り立っている。シグナリングは、通信相手の検出、接続の確立、通信パラメータの交換など、実際の音声や映像のやり取りを始めるための「お膳立て」を行う部分だ。これは通常、WebSocketという技術を使って行われる。WebSocketはTCPプロトコルに基づき、ウェブブラウザとサーバー間で双方向の通信を確立する。一般的にウェブサイトが使っているHTTPSと同じポート443番を使い、リバースプロキシやファイアウォールなどの既存のウェブインフラを通り抜けることができるため、デバッグも比較的容易だ。ウェブサーバーの設定が正しく、証明書が有効であれば、シグナリングは問題なく機能することが多い。

一方、メディア通信は、実際の音声や映像データをブラウザとサーバー間で直接やり取りする部分だ。これはRTP over UDPというプロトコルを使用する。UDPはTCPとは異なり、接続確立の手順がシンプルで、データが途中で失われても再送せずに次のデータを優先するため、リアルタイム性が求められる音声・映像ストリーミングに適している。重要なのは、メディア通信はリバースプロキシやCDN、VPNトンネルなどを一切通らないということだ。これは単なる設定ミスではなく、設計上の意図によるものだ。WebSocketのようなTCPベースの接続は、高頻度で大量のデータを送受信する30fpsの映像ストリームには適さないため、メディアデータはブラウザからサーバーへ直接UDPで送られるようになっている。この設計思想が、「シグナリングはうまくいっているのに映像が出ない」という現象の根本的な原因を理解する上で非常に重要となる。シグナリングが正常に見えても映像が出ない場合、問題はウェブスタックではなく、メディア通信経路にあると考えるべきだ。メディア通信経路に関する情報は、SDP(Session Description Protocol)というデータに含まれる「ICE候補リスト」が全てを物語っている。これは「私(サーバー)はこれらのアドレスで到達可能です」というリストだ。

最初の落とし穴は「ICE候補が嘘をつく」という問題だ。クラウド仮想マシン(VM)は、通常、プライベートIPアドレス(例えば172.31.x.xのような内部ネットワーク用アドレス)を持っている。しかし、そのVMがインターネットと通信する際には、クラウドプロバイダのNAT(Network Address Translation)層によって、外部向けのパブリックIPアドレスに変換される。VM自身は、自分のパブリックIPアドレスを知らないことが多い。そのため、VMが「私はどこにいますか?」と正直にICE候補を収集すると、外部からは到達不可能なプライベートIPアドレスを報告してしまう。結果として、シグナリングは成功し、ICEチェックは行われるものの、すべて到達不可能なアドレスに向けられるため、メディア接続は永遠に確立しない。ログにも明確なエラーは出ず、ひたすら接続待ちの状態が続く。

この問題の解決策は二つある。一つは「STUNサーバーを利用する」方法だ。サーバーが公開されているSTUNサーバーにアクセスし、「このリクエストはどのパブリックIPから来たように見えますか?」と尋ね、その回答を自分のパブリックIPとして広告する。これは一般的な解決策として機能するが、NATの種類によっては、STUNサーバーから得られたポートと実際に通信可能なポートが異なる場合があり、再び接続が失敗することがある。もう一つのより確実な方法は、「サーバーに自身のパブリックIPを明示的に教える」ことだ。クラウドプロバイダが、VMのプライベートIPとパブリックIPを1対1でマッピングしている場合、サーバーに直接パブリックIPを教えてしまえば、NATの挙動に左右されずに常に正しいICE候補を通知できる。これは設定エンジンを通じて一度だけ設定することで、確実なメディア接続を実現できる。重要なのは、サーバーがNATの背後にあるという事実を、設定上の重要な要素として意図的に扱うことだ。

二つ目の落とし穴は、「アプリケーションとは無関係なファイアウォール」の問題だ。クラウドプロバイダは、通常二種類のファイアウォールを提供している。一つはVMのOS上で動作するファイアウォール(ufwやiptablesなど)で、これは開発者がよくチェックするものだ。もう一つは、VMインスタンスの手前に位置するプロバイダレベルのファイアウォールで、一般に「セキュリティグループ」と呼ばれる。このセキュリティグループは、デフォルトでほとんどのポートが閉じていることが多い。例えば、ウェブサーバー用の443番ポートは開いていても、WebRTCメディア通信に必要なUDPポート範囲は閉じられている可能性がある。

この問題も、シグナリングは正常に動作するため、ウェブ関連のセットアップが完了していると錯覚しやすい。しかし、メディア通信はUDPを使用し、ポート443とは異なる範囲を使うため、HTTPSが機能していることはメディア通信の可否とは関係ない。症状はTrap 1と同じく、「シグナリングは緑色なのに、ICEが『チェック中』で停止し、最終的に失敗する」というものだ。外見上は同じ失敗に見えるため、どちらが原因かを特定するのが難しい。区別する方法は、ブラウザのWebRTC internalsページ(Chromeではchrome://webrtc-internals)でICE候補リストを確認することだ。候補リストにプライベートアドレスしかなく、パブリックアドレスが見えない場合はTrap 1(サーバーが自分のパブリックIPを学習できていない)だ。もし正しいパブリックIPの候補が見えるのに、そのアドレスへの接続性チェックが失敗している場合はTrap 2(パケットがプロバイダのセキュリティグループによってブロックされている)だと判断できる。この二分法を知っているだけで、トラブルシューティングの方向性が大きく変わり、無駄な時間を費やすのを防ぐことができる。

三つ目の落とし穴は、「リバースプロキシがアイドル状態のソケットを切断する」問題だ。WebRTCのシグナリング用WebSocketは、メディア通信が始まると、ほとんどデータが流れないアイドル状態になることが多い。多くのリバースプロキシには、アイドル状態の接続を一定時間(例えば60秒)で自動的に切断するタイムアウト設定がある。プロキシは、アイドル状態のWebSocketを「放棄された接続」とみなして閉じてしまうのだ。しかし、ブラウザ側のタブは接続が切れたことに気づかないため、ユーザーから見るとストリームは継続しているように見える。その後、何らかの理由でシグナリングが必要になった際(例えば再接続、新しい参加者の追加、トラックの変更など)、すでに閉じられたソケットにメッセージを送ろうとしてエラーが発生する。

この問題に対する一般的な、しかし誤った解決策は、ブラウザ側から30秒ごとに小さなメッセージ(ハートビート)を送ることだ。しかし、ブラウザはバックグラウンドタブのJavaScriptタイマーを積極的にスロットリング(制限)するため、バックグラウンドに移動したタブからのハートビートは停止し、結局は接続が切れてしまう。より確実な解決策は、サーバー側からWebSocketプロトコルレベルのPingフレームを送信することだ。WebSocketプロトコルにはPingとPongフレームが定義されており、ブラウザはPingを受信すると自動的にPongで応答する。この自動応答はJavaScriptのタイマー抑制の影響を受けない。サーバーはPingを定期的に送り、Pongが返ってこなければ接続が切れたと判断できる。サーバー側で接続の生存確認を行うことで、ブラウザ側の複雑なロジックが不要になり、安定した接続維持が可能となる。

これらの落とし穴を踏まえると、WebRTCアプリケーションのデプロイは特定の順序で進めるべきだ。まず、シグナリングコードを書く前に、メディアパスが機能するかどうかを最優先で確認する。具体的には、どのUDPポートをメディア通信に使うのか、そしてそのポートがインターネットから到達可能であるかを、クラウドプロバイダの設定を確認して明らかにする。次に、サーバーが自分のパブリックIPアドレスを正しく広告するように、明示的に設定を行う。デフォルト設定に任せるのではなく、必須の項目として扱う。そして、テストは開発用PCだけでなく、Wi-Fiを切ったスマートフォンなど、異なるネットワーク環境から行うべきだ。開発用PCと同じネットワーク環境では、実際には問題がある接続でも一時的に機能してしまうことがあるためだ。これらすべてが機能するのを確認してから、ようやく再接続ロジックなどのアプリケーションコードの構築に取り掛かる。動かないメディアパスの上で再接続ロジックをテストしても、何が原因で失敗しているのか切り分けが非常に困難になるからだ。この順序を守ることで、デプロイ時の多くの問題を未然に防ぎ、スムーズな開発が可能となる。

関連コンテンツ

関連IT用語

関連ITニュース