Websocket error with NodeBB running through a proxy server

Technical Support
  • I'm trying to run NodeBB through a node.js reverse proxy (https://docs.nodebb.org/configuring/proxies/node/)

    I've followed every tutorial, hint/tip I can but am still experiencing issues with websocket connections to my NodeBB server, causing session problems, can't log in etc.

    I hope someone can point me in the right direction to fix this.

    My setup is as follows:

    App 1 - http://www.mywebsite.co.uk/

    • Node.js & Express
    • Routing for API & frontend website
    • Nothing out of the ordinary
    • Full code snippet at bottom of post

    I am using the 'http-proxy' npm module to proxy anyone who loads http://mywebsite.co.uk/forum to http://www.myforum.co.uk/forum

    This part is working, assets load as expected.
    However, there is a part of NodeBB which uses websockets to poll the forum for functionality purposes, user sessions. This part is not proxying correctly, or at least the NodeBB response is not correct and therefore giving me lots of errors:

    "You are accessing the forum from an unknown origin. This will likely
    result in websockets failing to connect. To fix this, set the "url"
    value in config.json to the URL at which you access the site. For
    more information, see this FAQ topic:
    https://community.nodebb.org/topic/13388"


    "Looks like your connection to NodeBB was lost, please wait while we
    try to reconnect."

    And, in the network panel, lots of 'pending' requests which eventually fail with an empty response from NodeBB.

    http://mywebsite.co.uk/forum/socket.io/?EIO=3&transport=polling&t=MgJQSMk



    App 2 - http://www.myforum.co.uk/forum

    This app is a basic NodeBB installation running, with one plugin - (https://github.com/julianlam/nodebb-plugin-session-sharing)

    The config JSON file looks like this (note the URL is my frontend app's URL as per the instructions when proxying.

    {
        "url": "http://www.mywebsite.co.uk/forum",
        "secret": "secret",
        "database": "postgres",
        "port": "4567",
        "postgres": {
            "host": "HOST",
            "port": "PORT",
            "password": "PASSWORD",
            "database": "DATABASE"
        }
    }
    


    I'd really appreciate if anyone can help out. Thanks.



    App 1 code:

    // app
    const express = require("express");
    const app = express();
    
    app.use(require('cookie-parser')());
    app.use(require('body-parser').urlencoded({ extended: true }));
    app.use(require('express-session')({ secret: 'secret', resave: true, saveUninitialized: true }));
    
    //
    app.use((req, res, next) => {
      res.setHeader("Access-Control-Allow-Origin", "*");
      res.setHeader(
        "Access-Control-Allow-Methods",
        "OPTIONS, GET, POST, PUT, PATCH, DELETE"
      );
      res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
      next();
    });
    
    // serve the content
    app.use(express.static("dist"));
    
    // Frontend
    app.set('view engine', 'pug');
    
    // serve out the api
    // app.use ...
    
    // Server set up
    const httpProxy = require('http-proxy');
    const HttpProxyRules = require('http-proxy-rules');
    
    // Forum urls
    let rules = {
      rules: {
        '/forum': 'http://www.myforum.co.uk/forum',
        '/forum/*': 'http://www.myforum.co.uk/forum',
      },
    };
    
    const proxyRules = new HttpProxyRules(rules);
    const proxy = httpProxy.createProxy();
    
    app.use(function (req, res, next) {
      try {
        if (req.url.includes("socket.io") === true) {
          // console.log("SOCKET.IO", req.url)
          return proxy.web(req, res, {
            target: 'wss://www.myforum.co.uk',
            ws: true,
            changeOrigin: true
          }, function (e) {
            // console.log('PROXY ERR', e)
            // next();
          });
    
        } else {
          var target = proxyRules.match(req);
          if (target) {
            // console.log("TARGET", target, req.url)
            return proxy.web(req, res, {
              target: target,
              changeOrigin: true
            }, function (e) {
              // console.log('PROXY ERR', e)
            });
          } else {
            next();
          }
        }
      } catch (e) {
        // res.sendStatus(500);
        res.json({ error: e });
      }
    });
    
    // Frontend routes
    // app.use ...
    
    // HTTP
    const http = require('http');
    
    // Create server
    mainserver = http.createServer(app);
    
    const PORT = process.env.PORT || 3000;
    mainserver.listen(PORT);
    mainserver.on('listening', onListening);
    mainserver.on('error', function (error, req, res) {
      let json;
      console.log('proxy error', error);
      if (!res.headersSent) {
        res.writeHead(500, { 'content-type': 'application/json' });
      }
    
      json = { error: 'proxy_error', reason: error.message };
      res.end(JSON.stringify(json));
    });
    
    function onListening() {
      console.log(`Listening on :${PORT}`);
    }
    
  • To resolve this, I changed the Proxy module I was using. It uses less code to implement and works with websockets. I think the NodeBB Node.js proxy documentation is out of date so I'll be submitting a PR with the new method I found.

    Working code is as follows:

    /**
     * Proxy forum
     */
    const proxy = require('http-proxy-middleware');
    
    // Forum Proxy
    app.use(
      '/forum',
      proxy({
        target: 'http://www.myforum.co.uk/forum',
        changeOrigin: true,
        ws: true,
      })
    );
    

    Another potential GOTCHA here is that this Prixy setup needs to be declared ABOVE the 'body-parser' module (if it's being used). The wrong order will prevent post requests from being proxied.


Suggested Topics