Search code examples
node.jsexpresssocketswebsocketsocket.io

Getting SocketIO to work on HTTPS in NodeJs Express


Hello everyone,

I've got a problem after pushing both my NodedJs apps to my ubuntu server (serving sites in nginx with https). In my local environment with using localhost, the connections are established sucessfully, however after pushing to the server I get following response inside my second app (the admin) admin.domainname.com inside the client JavaScript console:

manager.js:108 WebSocket connection to 'wss://domainname.com/socket.io/?EIO=4&transport=websocket' failed

Heres the part where I establish the socket in my first app running on domainname.com and sending a message:

/*rest of app.js file with express.js*/ 

const PORT = 3000;

const server = app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`)
})

const socket = socketIo(server);

socket.on('connection', (socket) => {
    console.log("connected")
    socket.emit("testmessage", "Hello World")
    socket.on('disconnect', () => {});
});

In the second app (admin.domainname.com) inside my client-side JavaScript I want to receive the message like so:

const socketdomainconstruct = () => {
    if (location.port) {
      return "http://localhost:3000"
    } else {
      return "https://domainname.com"
    }
  }
  
const socket = io(socketdomainconstruct(), { transports: ['websocket', 'polling', 'flashsocket'] 

socket.on("connect", () => {
  console.log("connected");
  socket.on("testmessage", (message) => {
    console.log(message);
  });
});

For explanation, when i use http://localhost:3000 inside above socket.io instance, I can connect sucessfully and everything works as espected, so no issues with the overall structure. Only once i switch to https I get the error, which i dont seem to understand, since nothing changed.

Potential Issues could lay in:

  • The way I initialize the Socket server (maybe it can't be accessed over https, but this would be weird, because the nodejs app is ran inside nginx with valid ssl certificate, maybe I need to also include the configuration for the Socket.io server??)
  • Something with Cors => but I dont get any errors and all my Headers are set (Both Apps basically have the same) => see below:
app.use((req, res, next) => {

  res.removeHeader('Server');
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
  res.setHeader('Pragma', 'no-cache');
  res.setHeader('Expires', '0');
  res.setHeader('Last-Modified', new Date().toUTCString());
  res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.socket.io code.jquery.com cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; img-src 'self' bing.com; connect-src 'self' *");
  res.setHeader('Permission-Policy', 'accelerometer=(), camera=(), microphone=(), geolocation=(), gyroscope=()');
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'deny');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  next();
  
});

I hope that someone can help me in clarifying / isolate the problem, or even solve it with me, for reference I will also include helpful links from Socket.io

Thanks in advance for any help and tips you can provide, this would be really helpful for me!

Cheers Raffael


Solution

  • The problem was fixed by first adding the correct cors option to the SocketIO server object:

    socketIo(server, {cors: {origin: ["https://admin.domainname.com", "http://localhost:3000"]}}); // more than one origin can be incuded with array style writing...
    

    See more in the docs on SocketIO:

    Also I had to set up Nginx Proxy correctly, like so:

    location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
    
            proxy_pass http://admin-container:4000; #localhost in case of not running the app inside a Docker container
    
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade; #important part for Websocket connection to work
            proxy_set_header Connection 'upgrade'; #important part for Websocket connection to work
            proxy_cache_bypass $http_upgrade;
       }
    

    This can also be seen in the SocketIO docs, it is also duable for Apache and Caddy2 not only Nginx!

    Overall this problem could have been prevented by correctly reading the docs, they are quite good, thanks for everybodys help, wish you a great day, and hopefully the problem solving may help other persons with similar problems

    My setup was

    • Node V.18 lts Apps running in Docker containers
    • Nginx lts
    • Ubuntu

    Cheers Raffael