2026年7月4日

2026年7月4日

Envoy でWebSocket対応する手順|upgrade_configs の設定・タイムアウト・動作確認

Envoy で WebSocket を通すには、HTTP コネクションマネージャー(HCM)に upgrade_configs で websocket を許可し、長時間つながる接続がタイムアウトで切られないようルートの設定を見直します。Envoy は既定では WebSocket の Upgrade を通さないため、この設定が無いとハンドシェイクの段階で失敗します。この記事では、最小構成の設定ファイル、WebSocket のパスだけを許可する書き方、タイムアウトの考え方、TLS(wss://)の扱い、動作確認の方法を順に説明します。

前提:WebSocket が Envoy を通る仕組み

WebSocket は、最初に通常の HTTP/1.1 リクエストとして Upgrade: websocket と Connection: Upgrade のヘッダーを送り、サーバーが 101 Switching Protocols を返すと、その TCP 接続が双方向の通信路に切り替わります。

Envoy はこの Upgrade を「許可された種類だけ」転送します。許可されていないと、Upgrade ヘッダーを扱わず通常のリクエストとして処理されるため、クライアントは 101 を受け取れずに接続エラーになります。症状としては、ブラウザの開発者ツールで WebSocket の接続が失敗し、ステータスが 101 以外(404 や 400、あるいは通常の 200)になっているのが典型です。

なお、WordPress の PHP は WebSocket サーバーとして常駐する仕組みではないため、チャットや通知などの WebSocket は Node.js などの別アプリで動かし、Envoy で WordPress と同じドメインの特定パスに振り分ける構成がよく使われます。

手順1:最小構成の設定ファイルを書く

次の例は、8080 番で受けて、/ws で始まるパスを WebSocket アプリへ、それ以外を WordPress へ振り分けるものです。Envoy の v3 API の書式です。

static_resources:
  listeners:
  - name: listener_http
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          upgrade_configs:
          - upgrade_type: websocket
          route_config:
            name: local_route
            virtual_hosts:
            - name: site
              domains: ["*"]
              routes:
              - match: { prefix: "/ws" }
                route:
                  cluster: ws_app
                  timeout: 0s
              - match: { prefix: "/" }
                route:
                  cluster: wordpress
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

  clusters:
  - name: ws_app
    connect_timeout: 5s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: ws_app
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: ws-app, port_value: 3000 }
  - name: wordpress
    connect_timeout: 5s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: wordpress
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address: { address: wordpress, port_value: 80 }

要点は2つです。

  • upgrade_configs の upgrade_type: websocket が、WebSocket の Upgrade を許可する設定です。HCM に書くと、そのリスナーのすべてのルートで許可されます。
  • ルートの順番が意味を持ちます。上から順に照合されるので、/ws を / より前に置きます。逆にすると、すべてが WordPress へ流れます。

ws-app や wordpress は Docker Compose のサービス名を想定したホスト名です。実際の環境に合わせて書き換えてください。

手順2:WebSocket を特定のルートだけに絞る

HCM で許可すると、WordPress 側のパスにも Upgrade が通ります。害が出ることは少ないものの、意図しない経路を減らしたい場合は、ルート単位で許可する書き方もあります。RouteAction の upgrade_configs を使い、HCM 側の記述を外します。

              - match: { prefix: "/ws" }
                route:
                  cluster: ws_app
                  timeout: 0s
                  upgrade_configs:
                  - upgrade_type: websocket

HCM とルートの両方に書いた場合の優先関係や、enabled フィールドでの個別の無効化については、使っている Envoy の版のドキュメントで確認してください。

手順3:タイムアウトを見直す

WebSocket は長時間つながりっぱなしになるため、通常の HTTP 向けのタイムアウトがそのまま当たると途中で切れます。Envoy には主に次の2種類が関係します。

  • **ルートのタイムアウト(timeout)**:既定では15秒です。上の例では WebSocket のルートで 0s にして無効にしています。
  • ストリームのアイドルタイムアウト:一定時間データが流れないと切断するもので、HCM の stream_idle_timeout(既定は5分)や、ルートごとの idle_timeout で調整します。

アイドルタイムアウトを無効にすると、クライアントが黙って消えた接続が残り続け、接続数を圧迫します。おすすめは、アイドルタイムアウトは有限のまま残し、アプリ側で数十秒ごとに ping/pong を送って通信を絶やさない方法です。Envoy の手前にクラウドのロードバランサーがある場合は、そちらのアイドルタイムアウトのほうが短いことが多いので、あわせて確認してください。

手順4:TLS(wss://)を Envoy で終端する

ブラウザから wss:// でつなぐ場合は、Envoy のリスナーに TLS を設定します。HTTPS のサイトから平文の ws:// へつなぐと、混在コンテンツとしてブラウザに止められるので、本番では wss:// が前提です。

    filter_chains:
    - transport_socket:
        name: envoy.transport_sockets.tls
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
          common_tls_context:
            tls_certificates:
            - certificate_chain: { filename: "/etc/envoy/certs/fullchain.pem" }
              private_key: { filename: "/etc/envoy/certs/privkey.pem" }
      filters:
      # 以下は手順1と同じ http_connection_manager

秘密鍵のファイルは Envoy の実行ユーザーだけが読める権限にし、設定ファイルと一緒にリポジトリへ入れないようにします。HTTP/2 上で WebSocket を扱う方式(RFC 8441)にも Envoy は対応していますが、追加の設定が必要で、クライアントの対応状況にも差があります。まずは HTTP/1.1 での Upgrade を動かしてから検討するのが無難です。

手順5:設定を検査して動作を確認する

起動前に、設定ファイルの検査モードで書式の誤りを確かめます。

envoy --mode validate -c /etc/envoy/envoy.yaml

起動後は、ハンドシェイクだけを curl で確かめられます。101 が返れば Upgrade は通っています。

curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  http://localhost:8080/ws

実際のメッセージのやり取りまで試すなら、websocat などの WebSocket クライアントや、ブラウザの開発者ツールの「ネットワーク」タブで WS の通信を確認します。Envoy の管理インターフェースを有効にしている場合は、統計情報の upgrade 関連の値で、Upgrade された接続の数を確認できます。管理インターフェースは外部に公開せず、ローカルからのみ接続できるようにしてください。

つまずきやすい点

  • 101 ではなく 404 になる:ルートの順番や prefix が違い、WebSocket アプリに届いていません。
  • 101 ではなく 200 や 400 になる:upgrade_configs が効いていないか、WebSocket アプリ側がそのパスで待ち受けていません。
  • 数十秒〜数分で切れる:ルートのタイムアウト、アイドルタイムアウト、前段のロードバランサーのいずれかです。
  • 接続はできるが認証が効かない:WebSocket アプリ側で Origin ヘッダーの確認と、ログイン状態(トークンや Cookie)の検証を行ってください。Envoy の設定だけでは利用者の認可はできません。

Envoy は更新が頻繁で、フィールドの追加や既定値の見直しが行われます(2026年10月現在)。設定を本番に入れる前に、使う版のドキュメントで各フィールドを確認し、ステージングで同じ構成を試してから切り替えてください。

まとめ

  • Envoy は既定で WebSocket の Upgrade を通さないため、HCM またはルートの upgrade_configs で websocket を許可します。
  • /ws などのルートは / より前に置き、WebSocket のルートではタイムアウトを見直します。
  • アイドルタイムアウトは有限のまま、アプリの ping/pong で接続を維持するのが扱いやすい方法です。
  • 本番は wss:// で、TLS は Envoy で終端し、秘密鍵の権限に注意します。
  • envoy --mode validate と curl のハンドシェイク確認で、101 が返るかを確かめます。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲