T-CREATOR

WebSocket が「200 OK で Upgrade されない」原因と対処:プロキシ・ヘッダー・TLS の落とし穴

WebSocket が「200 OK で Upgrade されない」原因と対処:プロキシ・ヘッダー・TLS の落とし穴

WebSocket の実装で最も混乱するエラーのひとつが、「HTTP 200 OK が返ってくるのに、なぜか Upgrade されず WebSocket 接続が確立しない」という現象です。この記事では、WebSocket が正常に Upgrade されない原因を徹底解説し、プロキシ・HTTP ヘッダー・TLS 設定など、見落としがちな落とし穴とその対処法をご紹介します。

初めて WebSocket を実装する方や、本番環境で予期せぬエラーに遭遇している方にとって、この記事が問題解決の糸口になれば幸いです。

背景

WebSocket 接続の仕組み

WebSocket は、HTTP/1.1 のアップグレード機能を利用して、クライアントとサーバー間で双方向通信を確立するプロトコルです。通常の HTTP 通信とは異なり、一度接続を確立すれば、サーバーからクライアントへのプッシュ通知や、リアルタイムなデータ交換が可能になります。

WebSocket 接続の確立には、以下のような HTTP Upgrade ハンドシェイク が必要です。

mermaidsequenceDiagram
  participant client as クライアント
  participant server as サーバー
  client->>server: HTTP GET /ws<br/>Connection: Upgrade<br/>Upgrade: websocket
  alt Upgrade 成功
    server->>client: HTTP 101 Switching Protocols<br/>Upgrade: websocket
    client->>server: WebSocket メッセージ送受信
    server->>client: WebSocket メッセージ送受信
  else Upgrade 失敗
    server->>client: HTTP 200 OK<br/>(通常の HTTP レスポンス)
    Note over client: WebSocket 接続失敗
  end

上記の図から分かるように、WebSocket 接続が成功するには HTTP 101 Switching Protocols というステータスコードが必須です。しかし、何らかの原因で 101 ではなく 200 OK が返ってくると、WebSocket への切り替えが行われず、接続が失敗してしまうのです。

WebSocket ハンドシェイクの必須要素

WebSocket 接続を確立するためには、クライアントとサーバー双方が正しいヘッダーを送信する必要があります。

クライアント側のリクエストヘッダー

typescript// クライアントが送信する必須ヘッダー
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

このリクエストには、Upgrade: websocketConnection: Upgrade が含まれています。これらのヘッダーが、サーバーに対して「WebSocket プロトコルへの切り替えを要求している」ことを伝える重要な役割を果たします。

サーバー側のレスポンスヘッダー

typescript// サーバーが返すべき正しいレスポンス
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

サーバーは 101 Switching Protocols ステータスコードと共に、Upgrade および Connection ヘッダーを返す必要があります。また、Sec-WebSocket-Accept ヘッダーには、クライアントから送信された Sec-WebSocket-Key を元に計算された値が含まれます。

この一連のやり取りが正しく行われないと、WebSocket 接続は確立されず、200 OK が返ってきてしまうのです。

課題

200 OK が返ってくる典型的なケース

WebSocket 接続時に 200 OK が返ってくる主な原因は、以下の 3 つに分類できます。

#原因カテゴリ具体的な問題
1プロキシ・ロードバランサーUpgrade ヘッダーの削除、HTTP/1.0 へのダウングレード
2HTTP ヘッダーの不備必須ヘッダーの欠落、大文字小文字の誤り
3TLS/SSL 設定の問題プロトコルバージョンの不一致、証明書エラー

それぞれの原因について、詳しく見ていきましょう。

プロキシ・ロードバランサーによるヘッダー削除

Nginx や Apache、AWS ALB などのプロキシを経由する場合、Upgrade ヘッダーが削除されたり、書き換えられたりすることがあります。これは、プロキシが WebSocket を正しく認識していないために発生します。

mermaidflowchart LR
  client["クライアント"] -->|Upgrade: websocket| proxy["プロキシ<br/>(設定不備)"]
  proxy -->|Upgrade ヘッダー削除| server["WebSocket<br/>サーバー"]
  server -->|200 OK<br/>(通常の HTTP)| proxy
  proxy -->|200 OK| client

  style proxy fill:#ff9999
  style server fill:#ffcc99

上図のように、プロキシが Upgrade ヘッダーを削除すると、サーバーは通常の HTTP リクエストとして処理し、200 OK を返してしまいます。

HTTP ヘッダーの記述ミス

WebSocket ハンドシェイクでは、ヘッダー名の大文字小文字や、値の正確性が重要です。以下のような些細なミスが原因で、接続が失敗することがあります。

typescript// ❌ 誤った記述例
Connection: keep-alive, Upgrade  // Upgrade が最初に来ていない
Upgrade: WebSocket              // 小文字で "websocket" と書くべき
Sec-Websocket-Key: ...          // "WebSocket" の大文字小文字が誤り

これらのヘッダーは、RFC 6455 で厳密に定義されており、仕様に従わない記述は拒否されます。

TLS/SSL 終端での問題

HTTPS (wss://) 環境では、TLS/SSL の終端処理がプロキシで行われることがあります。この場合、プロキシからバックエンドサーバーへの通信が HTTP にダウングレードされ、WebSocket の Upgrade が正しく処理されないことがあります。

mermaidflowchart TB
  client["クライアント<br/>wss://example.com/ws"] -->|HTTPS<br/>(TLS 暗号化)| proxy["リバースプロキシ<br/>(TLS 終端)"]
  proxy -->|HTTP<br/>(平文)| server["WebSocket サーバー<br/>ws://localhost:3000"]

  style proxy fill:#99ccff
  style server fill:#ffcc99

TLS 終端後、プロキシが HTTP/1.0 で通信したり、Upgrade ヘッダーを正しく転送しなかったりすると、200 OK が返ってくる原因となります。

解決策

プロキシ設定での Upgrade ヘッダー処理

Nginx での設定

Nginx をリバースプロキシとして使用する場合、以下のように設定することで WebSocket の Upgrade を正しく処理できます。

nginx# Nginx で WebSocket をサポートする設定
location /ws {
  proxy_pass http://backend;

  # Upgrade ヘッダーを転送
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";

  # その他の推奨ヘッダー
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
}

この設定で重要なのは、以下の 3 点です。

  1. proxy_http_version 1.1: HTTP/1.1 を使用することで、Upgrade ヘッダーのサポートを有効化します
  2. proxy_set_header Upgrade $http_upgrade: クライアントから送られた Upgrade ヘッダーをそのまま転送します
  3. proxy_set_header Connection "upgrade": Connection ヘッダーを "upgrade" に設定します

これらの設定により、Nginx は WebSocket のハンドシェイクを正しく中継できるようになります。

Apache での設定

Apache の場合、mod_proxy_wstunnel モジュールを有効化する必要があります。

apache# Apache で WebSocket をサポートする設定
<VirtualHost *:443>
  ServerName example.com

  # WebSocket 用のプロキシ設定
  ProxyRequests Off
  ProxyPreserveHost On

  # WebSocket エンドポイント
  <Location "/ws">
    ProxyPass "ws://localhost:3000/ws"
    ProxyPassReverse "ws://localhost:3000/ws"
  </Location>

  # 通常の HTTP エンドポイント
  <Location "/">
    ProxyPass "http://localhost:3000/"
    ProxyPassReverse "http://localhost:3000/"
  </Location>

  SSLEngine on
  SSLCertificateFile /path/to/cert.pem
  SSLCertificateKeyFile /path/to/key.pem
</VirtualHost>

Apache では、ws:​/​​/​ スキームを指定することで、自動的に WebSocket プロトコルとして処理されます。

AWS ALB での設定

AWS Application Load Balancer を使用する場合、ターゲットグループの設定で WebSocket を有効化します。

bash# AWS CLI で WebSocket を有効化
aws elbv2 modify-target-group-attributes \
  --target-group-arn arn:aws:elasticloadbalancing:region:account-id:targetgroup/my-targets/1234567890 \
  --attributes Key=deregistration_delay.timeout_seconds,Value=120

ALB では、デフォルトで WebSocket がサポートされていますが、以下の点に注意が必要です。

#設定項目推奨値理由
1アイドルタイムアウト60 秒以上WebSocket は長時間接続を維持するため
2ヘルスチェックパス通常の HTTP エンドポイントWebSocket エンドポイントではヘルスチェックが失敗する
3スティッキーセッション有効同じバックエンドに接続を維持するため

HTTP ヘッダーの正しい記述方法

Node.js + ws ライブラリでの実装

Node.js で WebSocket サーバーを実装する際の正しい例をご紹介します。

typescript// 必要なパッケージのインポート
import { WebSocketServer } from 'ws';
import http from 'http';

まず、WebSocket サーバーを作成する前に、HTTP サーバーを準備します。

typescript// HTTP サーバーの作成
const server = http.createServer((req, res) => {
  // 通常の HTTP リクエスト処理
  if (req.url === '/health') {
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('OK');
  } else {
    res.writeHead(404);
    res.end('Not Found');
  }
});

次に、WebSocket サーバーを HTTP サーバーにアタッチします。

typescript// WebSocket サーバーの作成
const wss = new WebSocketServer({
  server,
  path: '/ws', // WebSocket エンドポイントのパス
});

// 接続イベントのハンドリング
wss.on('connection', (ws, req) => {
  console.log(
    'WebSocket 接続確立:',
    req.socket.remoteAddress
  );

  // メッセージ受信時の処理
  ws.on('message', (data) => {
    console.log('受信:', data.toString());
    ws.send(`Echo: ${data}`);
  });

  // エラー処理
  ws.on('error', (error) => {
    console.error('WebSocket エラー:', error);
  });

  // 接続終了時の処理
  ws.on('close', () => {
    console.log('WebSocket 接続終了');
  });
});

最後に、サーバーを起動します。

typescript// サーバーの起動
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`サーバー起動: http://localhost:${PORT}`);
  console.log(`WebSocket: ws://localhost:${PORT}/ws`);
});

この実装により、ws ライブラリが自動的に正しい Upgrade ヘッダーを処理してくれます。

クライアント側での接続エラーハンドリング

クライアント側でも、エラーハンドリングを適切に実装することが重要です。

typescript// WebSocket クライアントの実装
class WebSocketClient {
  private ws: WebSocket | null = null;
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 5;

  constructor(private url: string) {}

  connect(): void {
    try {
      this.ws = new WebSocket(this.url);
      this.setupEventHandlers();
    } catch (error) {
      console.error('WebSocket 接続エラー:', error);
      this.handleReconnect();
    }
  }

イベントハンドラーの設定では、各種エラーを適切に処理します。

typescript  private setupEventHandlers(): void {
    if (!this.ws) return;

    // 接続成功時
    this.ws.onopen = (event) => {
      console.log('WebSocket 接続成功');
      this.reconnectAttempts = 0;
    };

    // メッセージ受信時
    this.ws.onmessage = (event) => {
      console.log('受信:', event.data);
    };

    // エラー発生時
    this.ws.onerror = (event) => {
      console.error('WebSocket エラー:', event);
    };

    // 接続終了時
    this.ws.onclose = (event) => {
      console.log('WebSocket 接続終了:', event.code, event.reason);

      // 正常終了でない場合は再接続を試みる
      if (event.code !== 1000) {
        this.handleReconnect();
      }
    };
  }

再接続のロジックを実装します。

typescript  private handleReconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('最大再接続回数に達しました');
      return;
    }

    this.reconnectAttempts++;
    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);

    console.log(`${delay}ms 後に再接続します (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);

    setTimeout(() => {
      this.connect();
    }, delay);
  }
}

このように実装することで、接続失敗時の詳細な情報を取得し、自動的に再接続を試みることができます。

TLS/SSL 設定の最適化

プロキシでの TLS 終端設定

Nginx で TLS 終端を行う場合、以下のような設定が推奨されます。

nginx# TLS 終端を含む WebSocket 設定
server {
  listen 443 ssl http2;
  server_name example.com;

  # SSL 証明書の設定
  ssl_certificate /etc/nginx/ssl/cert.pem;
  ssl_certificate_key /etc/nginx/ssl/key.pem;

  # SSL プロトコルの設定
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers HIGH:!aNULL:!MD5;
  ssl_prefer_server_ciphers on;

  # WebSocket エンドポイント
  location /ws {
    proxy_pass http://localhost:3000;

    # WebSocket 用のヘッダー設定
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    # クライアント情報の転送
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;

    # タイムアウト設定
    proxy_connect_timeout 7d;
    proxy_send_timeout 7d;
    proxy_read_timeout 7d;
  }
}

この設定では、TLS 終端後もバックエンドへ正しく Upgrade ヘッダーが転送されるようになっています。

Node.js での HTTPS サーバー実装

Node.js で直接 HTTPS + WebSocket サーバーを実装する場合は、以下のようにします。

typescript// HTTPS + WebSocket サーバーの実装
import https from 'https';
import fs from 'fs';
import { WebSocketServer } from 'ws';

// SSL 証明書の読み込み
const serverOptions = {
  cert: fs.readFileSync('/path/to/cert.pem'),
  key: fs.readFileSync('/path/to/key.pem'),
};

HTTPS サーバーを作成し、WebSocket サーバーをアタッチします。

typescript// HTTPS サーバーの作成
const server = https.createServer(
  serverOptions,
  (req, res) => {
    if (req.url === '/health') {
      res.writeHead(200, { 'Content-Type': 'text/plain' });
      res.end('OK');
    } else {
      res.writeHead(404);
      res.end('Not Found');
    }
  }
);

// WebSocket サーバーの作成
const wss = new WebSocketServer({
  server,
  path: '/ws',
});

// サーバーの起動
const PORT = 443;
server.listen(PORT, () => {
  console.log(
    `HTTPS + WebSocket サーバー起動: wss://localhost:${PORT}/ws`
  );
});

この実装により、TLS 暗号化された WebSocket 接続 (wss://) を直接提供できます。

具体例

エラーケース 1: Nginx で Upgrade ヘッダーが転送されない

エラーの症状

bash# ブラウザの開発者ツールで確認できるエラー
WebSocket connection to 'wss://example.com/ws' failed:
Error during WebSocket handshake:
Unexpected response code: 200

このエラーは、サーバーが 200 OK を返しているものの、101 Switching Protocols が返ってこないことを示しています。

原因の特定

bash# curl でヘッダーを確認
curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  https://example.com/ws

このコマンドの結果、以下のようなレスポンスが返ってくる場合、プロキシ設定に問題があります。

bash# ❌ 誤ったレスポンス例
HTTP/1.1 200 OK
Server: nginx/1.18.0
Content-Type: text/html
Content-Length: 612

正しい場合は、以下のようなレスポンスが返ってきます。

bash# ✅ 正しいレスポンス例
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

解決手順

以下の手順で Nginx の設定を修正します。

nginx# 1. Nginx 設定ファイルを開く
# /etc/nginx/sites-available/example.com

server {
  listen 443 ssl;
  server_name example.com;

  location /ws {
    # ❌ 修正前(この設定だと Upgrade されない)
    # proxy_pass http://localhost:3000;

    # ✅ 修正後
    proxy_pass http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

設定を保存したら、Nginx の設定をテストして再起動します。

bash# Nginx 設定のテスト
sudo nginx -t

# 設定に問題がなければ再起動
sudo systemctl reload nginx

再起動後、再度 curl でテストします。

bash# 修正後の動作確認
curl -i -N \
  -H "Connection: Upgrade" \
  -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  https://example.com/ws

# ✅ 101 Switching Protocols が返ってくれば成功

エラーケース 2: AWS ALB 経由で接続タイムアウトが発生

エラーの症状

javascript// クライアント側のエラーログ
WebSocket connection closed: CloseEvent {
  code: 1006,
  reason: "",
  wasClean: false
}

// エラーコード 1006 は「異常終了」を示す

このエラーは、WebSocket 接続が確立した後、一定時間で強制的に切断されることを示しています。

原因の特定

AWS ALB のデフォルト設定では、アイドルタイムアウトが 60 秒に設定されています。WebSocket 接続が 60 秒間データを送受信しないと、ALB が接続を切断してしまいます。

mermaidsequenceDiagram
  participant client as クライアント
  participant alb as AWS ALB
  participant server as サーバー

  client->>alb: WebSocket 接続確立
  alb->>server: 接続転送
  Note over client,server: 60 秒間アイドル状態
  alb->>client: 接続強制切断<br/>(タイムアウト)
  Note over client: CloseEvent 1006

解決手順

AWS ALB のアイドルタイムアウトを延長します。

bash# AWS CLI でタイムアウトを 300 秒に延長
aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:loadbalancer/app/my-alb/1234567890abcdef \
  --attributes Key=idle_timeout.timeout_seconds,Value=300

また、クライアント側で定期的に Ping を送信することで、接続を維持できます。

typescript// クライアント側で定期的に Ping を送信
class WebSocketClientWithPing {
  private ws: WebSocket | null = null;
  private pingInterval: NodeJS.Timeout | null = null;

  connect(url: string): void {
    this.ws = new WebSocket(url);

    this.ws.onopen = () => {
      console.log('WebSocket 接続成功');
      this.startPing();
    };

    this.ws.onclose = () => {
      console.log('WebSocket 接続終了');
      this.stopPing();
    };
  }

Ping の送信ロジックを実装します。

typescript  private startPing(): void {
    // 30 秒ごとに Ping を送信
    this.pingInterval = setInterval(() => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.ws.send(JSON.stringify({ type: 'ping' }));
        console.log('Ping 送信');
      }
    }, 30000);
  }

  private stopPing(): void {
    if (this.pingInterval) {
      clearInterval(this.pingInterval);
      this.pingInterval = null;
    }
  }
}

サーバー側でも Ping に対応します。

typescript// サーバー側で Ping に応答
wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    const message = JSON.parse(data.toString());

    // Ping メッセージに Pong で応答
    if (message.type === 'ping') {
      ws.send(JSON.stringify({ type: 'pong' }));
      console.log('Pong 送信');
    }
  });
});

エラーケース 3: Docker コンテナ内での接続失敗

エラーの症状

bash# Docker ログに表示されるエラー
Error: connect ECONNREFUSED 127.0.0.1:3000
  at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1144:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 3000
}

このエラーは、Docker コンテナ内のサービスに接続できないことを示しています。

原因の特定

Docker コンテナ内でサービスが localhost127.0.0.1 にバインドされている場合、コンテナ外部からアクセスできません。

yaml# ❌ 誤った Docker Compose 設定
version: '3.8'
services:
  websocket:
    image: node:18
    command: node server.js
    ports:
      - '3000:3000'
    # サーバーが 127.0.0.1 にバインドされている場合、
    # コンテナ外部からアクセスできない

解決手順

サーバーを 0.0.0.0 にバインドするように変更します。

typescript// Node.js サーバーの修正
import { WebSocketServer } from 'ws';
import http from 'http';

const server = http.createServer();
const wss = new WebSocketServer({ server, path: '/ws' });

// ❌ 修正前
// server.listen(3000, 'localhost');

// ✅ 修正後: すべてのネットワークインターフェースにバインド
server.listen(3000, '0.0.0.0', () => {
  console.log(
    'WebSocket サーバー起動: ws://0.0.0.0:3000/ws'
  );
});

Docker Compose の設定も確認します。

yaml# ✅ 正しい Docker Compose 設定
version: '3.8'
services:
  websocket:
    image: node:18
    command: node server.js
    ports:
      - '3000:3000'
    environment:
      - HOST=0.0.0.0
      - PORT=3000
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

変更後、コンテナを再起動します。

bash# Docker コンテナの再起動
docker-compose down
docker-compose up -d

# ログを確認
docker-compose logs -f websocket

接続をテストします。

bash# ホストマシンから接続テスト
wscat -c ws://localhost:3000/ws

# 接続成功メッセージが表示されれば OK
Connected (press CTRL+C to quit)
>

デバッグツールの活用

WebSocket の問題をデバッグする際に役立つツールをご紹介します。

wscat でのテスト

bash# wscat のインストール
npm install -g wscat

# WebSocket 接続のテスト
wscat -c ws://localhost:3000/ws

# HTTPS の場合
wscat -c wss://example.com/ws

wscat を使うことで、ブラウザを使わずに WebSocket 接続をテストできます。

Chrome DevTools でのデバッグ

ブラウザの開発者ツールでは、WebSocket の通信内容を詳しく確認できます。

javascript// Chrome DevTools の Network タブで確認できる情報
// 1. General セクション
Request URL: wss://example.com/ws
Request Method: GET
Status Code: 101 Switching Protocols

// 2. Response Headers セクション
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

// 3. Request Headers セクション
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

これらの情報を確認することで、どのヘッダーが不足しているか、または誤っているかを特定できます。

まとめ

WebSocket が「200 OK で Upgrade されない」問題は、以下の 3 つの主要な原因に起因することが分かりました。

  1. プロキシ・ロードバランサーの設定不備: Nginx、Apache、AWS ALB などで Upgrade ヘッダーが正しく転送されない
  2. HTTP ヘッダーの記述ミス: 必須ヘッダーの欠落や、大文字小文字の誤り
  3. TLS/SSL 設定の問題: プロトコルバージョンの不一致や、終端処理の誤り

それぞれの問題に対して、以下の対処法が有効です。

#問題カテゴリ対処法確認コマンド
1プロキシ設定proxy_http_version 1.1 と Upgrade ヘッダーの転送設定curl -i -N -H "Upgrade: websocket"
2ヘッダー記述RFC 6455 準拠の正確な記述Chrome DevTools の Network タブ
3TLS 設定TLS 1.2 以上、適切なタイムアウト設定openssl s_client -connect host:443
4タイムアウトPing/Pong による接続維持サーバーログでアイドル時間を確認
5Docker 環境0.0.0.0 へのバインドdocker-compose logs

WebSocket の実装では、クライアント・サーバー・プロキシの 3 層すべてで正しい設定が必要です。特に本番環境では、プロキシの設定が原因で接続が失敗することが多いため、インフラ設定の見直しが重要になります。

この記事でご紹介した対処法を順番に試していただくことで、「200 OK で Upgrade されない」問題を解決できるはずです。デバッグの際は、curl や wscat などのツールを活用し、どの層で問題が発生しているかを特定することが、早期解決への近道となります。

関連リンク