Discourseフォーラムの `/message-bus` またはロングポーリングエラーの修正方法

1. 問題

CDN で Discourse を構成すると、一般的なエラーが発生します。

/message-bus/204234de907442e8b77e153786a58e5b/poll
接続失敗 / タイムアウト / 不正なステータスコード

影響:

  • 通知が失敗する: 赤い点(新しい PM/返信)がリアルタイムで表示されなくなり、更新が遅れたり、まったく届かなくなったりする可能性があります。

  • リアルタイム更新が壊れる: 新しい投稿/いいね/投票が自動更新されなくなり、手動でのページ更新が必要になります。

  • ユーザーエクスペリエンスが低下する: 「接続が失われました」というメッセージが表示され、インタラクションが遅延します。

  • サーバー負荷の増加: フロントエンドがポーリングを繰り返し試行し、オリジンに圧力をかけます。

理由: Discourse は、リアルタイム通信を維持するためにロングポーリングを使用します。多くの CDN は、デフォルトのキャッシュ、短縮されたタイムアウト、チャレンジ/ファイアウォールチェック、またはロング接続でのバッファリングを強制し、中断やキャッシュされた応答を引き起こしてリアルタイム動作を壊します。


2. 一般的なアプローチ(安定性優先)

  • ドメイン分離: MessageBus を、オリジンに直接接続する専用ドメイン経由でルーティングします。

  • リバースプロキシ(Nginx)レイヤー:

    • /message-bus の CORS を有効にします。
    • プロキシバッファリングを無効にし、タイムアウトを緩和し、明示的にキャッシュを禁止します。
  • CDN レイヤー(まだ使用する場合):

    • /message-bus/* をキャッシュなし、タイムアウト延長、JS チャレンジ/CAPTCHA/レート制限なし、Cookie/認証情報パススルーで構成します。
    • または、CDN を完全にバイパスします。

3. 実装手順

1) Discourse 環境変数の構成

app.yml(通常は /var/discourse/containers/app.yml)を編集し、env:: の下に追加/変更します。

env:
  DISCOURSE_MESSAGE_BUS_REDIS_ENABLED: true
  DISCOURSE_LONG_POLLING_BASE_URL: "https://messagebus.example.com"

変更を適用します(公式デプロイメント):

cd /var/discourse
./launcher rebuild app

説明:

  • DISCOURSE_LONG_POLLING_BASE_URL は、フロントエンドに MessageBus ドメインを使用するように指示します。
  • REDIS_ENABLED は有効のままにしておく必要があります。

2) DNS と証明書

  • messagebus.example.com を CDN をバイパスしてオリジンに直接ポイントします(ベストプラクティス)。
  • ドメインの有効な HTTPS 証明書を設定します。

3) Nginx(MessageBus ドメイン)リバースプロキシと CORS

messagebus.example.com の server ブロックに以下を追加または更新します。

location ^~ /message-bus {

    # (1) CORS 事前フライト(OPTIONS)を処理
    if ($request_method = OPTIONS) {
        add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain; charset=UTF-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
    }

    # (2) Discourse へのリバースプロキシ
    proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
    # または、スタンドアロンの場合:
    # proxy_pass http://127.0.0.1:3000;

    # (3) 重複する CORS ヘッダーを防止
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Credentials;
    proxy_hide_header Access-Control-Allow-Methods;
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Max-Age;

    # (4) 通常の CORS リクエスト用
    add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;

    # (5) ロングポーリング安定性設定
    proxy_read_timeout    120s;
    proxy_send_timeout    120s;
    proxy_connect_timeout 60s;

    proxy_buffering off;
    add_header X-Accel-Buffering no always;

    # (6) 明示的にキャッシュを禁止
    add_header Cache-Control "no-store, no-cache, must-revalidate" always;
}

:warning: セキュリティ上の注意: Access-Control-Allow-Credentials: true を使用する場合、Origin は * にすることはできません。正確なフォーラム ドメインと一致する必要があります。


4) CDN ルール(フォーラムがまだ CDN の背後にある場合)

推奨: これらのパスに対してキャッシュなし + タイムアウト延長 + WAF/レート制限のバイパスを設定します。

正規表現の例:

^/(session|login|message-bus|admin|u|users)(/|$)

ポリシー:

  • ブラウザ/ノードキャッシュなし(no-store/no-cache)。
  • アップストリーム/読み取り/アイドルタイムアウト ≥ 60–120 秒。
  • JS チャレンジ/CAPTCHA/ボット管理を無効にします。
  • Cookie と Authorization ヘッダーをパススルーします(削除しません)。

4. 成功の検証方法

1) ブラウザの開発者ツール → ネットワーク

フォーラムページで:

  • /message-bus/…/poll を観察します。
  • リクエストは ~20〜60 秒間「ハング」し、その後 200(空の場合あり)を返すはずです。
  • 次のポーリングリクエストが自動的にトリガーされます。

レスポンスヘッダーを確認します:

  • Access-Control-Allow-Origin: https://bbs.example.com
  • Cache-Control: no-store
  • Age、X-Cache: HIT、または CF-Cache-Status: HIT がない(キャッシュされていないことを意味します)。

一般的な問題:

  • 10 秒/30 秒のエラーが修正された → エッジ/オリジン タイムアウト。
  • 504/524: タイムアウト。
  • 499: 中間レイヤー切断。
  • 403/401: WAF/認証ブロック。

2) コマンドラインクイックプローブ(オプション)

接続性とヘッダーを確認します(完全なポーリングではありません):

curl -I "https://messagebus.example.com/message-bus/health-check" \
  -H "Origin: https://bbs.example.com"

注意: 実際のポーリングにはセッションコンテキストが必要ですが、これは CORS と接続性のみを検証します。

「いいね!」 1