さくらのVPS＋nginxで、mosquittoブローカーにMQTT over WebSocketで接続できない(AMQJS0007E Socket error:undefined.)


さくらのVPS上で、MQTT over
WebSocketでmosquittoブローカーにsubscribeし、Webページにsubscribeした内容を表示するプログラムを作ろうと思っていますが、firefoxの開発者ツール内で以下のエラーが出てしまいます。Webページでのsubscribeにはmqttws31.min.jsを使用しました。
VSCodeの拡張機能Live
Serverを使いローカルで動かしたときには問題なく動きます(追記：ローカルのnginxでも動きました)。個人的にはnginxのリバースプロキシ設定が怪しい気がしたのですが、どこが間違っているのか分かりませんでした…

どのようにすれば、さくらのVPS上でWebSocketを用いてMQTT通信ができるのでしょうか？初学者なので色々勘違いしているかもしれませんが、どなたかご教授いただけましたら幸いです。よろしくお願い致します。

【エラー内容】

[code]

    CONNECT TO : xxx.xxx.xxx.xxx : 9090
    ws://xxx.xxx.xxx.xxx:9090/ws のサーバーへの接続を確立できませんでした。
    ws://xxx.xxx.xxx.xxx:9090/ws のサーバーへの接続を確立できませんでした。
    CONNECTION FAILURE - AMQJS0007E Socket error:undefined.
    
[/code]

【環境】

  * さくらのVPS（OS：Ubuntu）
  * ブラウザ：Firefox(Chromeでも同様)
  * nginx/1.18.0

【行ったこと】

  * さくらのVPSでパケットフィルターの設定

    * Web | TCP 80/443
    * カスタム | TCP 9090
  * ufwの設定

[code]

    $ sudo ufw status
    Status: active
    
    To                         Action      From
    --                         ------      ----
    1883/tcp                   ALLOW       Anywhere
    22/tcp                     ALLOW       Anywhere
    80/tcp                     ALLOW       Anywhere
    9090/tcp                   ALLOW       Anywhere
    Nginx Full                 ALLOW       Anywhere
    OpenSSH                    ALLOW       Anywhere
    1883/tcp (v6)              ALLOW       Anywhere (v6)
    22/tcp (v6)                ALLOW       Anywhere (v6)
    80/tcp (v6)                ALLOW       Anywhere (v6)
    9090/tcp (v6)              ALLOW       Anywhere (v6)
    Nginx Full (v6)            ALLOW       Anywhere (v6)
    OpenSSH (v6)               ALLOW       Anywhere (v6)
    
[/code]

  * mosquitto.confの設定

[code]

    allow_anonymous true 
    
    listener 1883
    
    listener 9090
    protocol websockets
    
[/code]

  * mosquitto.logの確認

[code]

    1700453342: Config loaded from /etc/mosquitto/mosquitto.conf.
    1700453342: Opening ipv4 listen socket on port 1883.
    1700453342: Opening ipv6 listen socket on port 1883.
    1700453342: Opening websockets listen socket on port 9090.
    1700453342: mosquitto version 2.0.11 running
    
[/code]

  * /etc/nginx/conf.d/server.conf の設定

[code]

    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }
    
    server {
        listen 80;
        server_name localhost;
    
        location /ws {
            proxy_pass http://localhost:9090;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
    
[/code]

  * index.html（subscribe用のWebページ）

[code]

    <!DOCTYPE HTML>
    <html>
    <head>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript"></script>            
    </head>
    <body>
    
    <div id="output"></div>
    
    <script>
    var wsbroker = "xxx.xxx.xxx.xxx"; 
    var wsport = 9090;
    
    var client = new Paho.MQTT.Client(wsbroker, wsport, "/ws", "myclientid_" + parseInt(Math.random() * 100, 10));
    
    client.onConnectionLost = function (responseObject) {
      document.getElementById('output').innerHTML = "CONNECTION LOST - " + responseObject.errorMessage;
    };
    
    client.onMessageArrived = function (message) {
      var outputMessage =
        "Receive Topic: " + message.destinationName +
        ' PAYLOAD: ' + message.payloadString;
    
      document.getElementById('output').innerHTML = outputMessage;
      console.log(outputMessage);
    }
    
    var options = {
      timeout : 3,
      onSuccess: function () {
        var successMessage = "CONNECTION SUCCESS";
        document.getElementById('output').innerHTML = successMessage;
        console.log(successMessage);
        client.subscribe("topic/#");
      },
      onFailure: function (message) {
        var failureMessage = "CONNECTION FAILURE - " + message.errorMessage;
        document.getElementById('output').innerHTML = failureMessage;
        console.log(failureMessage);
      }
    };
    
    var connectMessage = "CONNECT TO : " + wsbroker + " : " + wsport;
    document.getElementById('output').innerHTML = connectMessage;
    console.log(connectMessage);
    
    client.connect(options);
    </script>
    
    </body>
    </html>
    
[/code]

【追記 2023/11/22】  
開発者ツールで、ローカルの場合とVPSでの場合それぞれの要求ヘッダーを見ると冒頭部分が異なりました。

  * ローカル

[code]

    GET /ws HTTP/1.1
    
[/code]

  * VPS

[code]

    GET /ws undefined
    
[/code]

