Ad

Socket.io Not Disconnecting Through Cloudflare/Nginx

I have a small web app that's served with Express.js and connects to the backend with Socket.io. To make it public, I'm using Nginx as a reverse proxy and then Cloudflare at the very front. The app relies on the disconnect event firing when the it's closed or reloaded to keep track of online users among other things. When going through Nginx and Cloudflare the disconnect event never fires on the backend. It does when developing locally.

Here's my Nginx config file:

server {
    listen 80;
    server_name colab.gq;
    server_name www.colab.gq;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header X-Forwarded-For $remote_addr;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/colab.gq/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/colab.gq/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

And here's a snippet of my server-side code:

io.on('connection', (socket) => {
  // Stuff here

  socket.on('disconnect', () => {
    console.log('disconnected!')
    // Stuff that relies on the disconnect event
  })
})

When the user closes the tab or reloads the page the disconnect event should fire, but it never does when passing the connection through Nginx and Cloudflare. Thanks in advance for any help!

UPDATE: It seems like a few seconds after reloading/closing the disconnect event finally registers.

Ad

Answer

You have to add upgrade headers to your socket io path in nginx configuration it like this,

    location ~* \.io {
      .. your configuration

      proxy_pass http://localhost:3000;
      proxy_redirect off;

      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }

Since you asked for a extension of the answer, first thing which you may already know is socket.io is a protocol that use Websockets protocol under the hood (so both are the same). As standards both Websockets protocols and HTTP protocols is listening on the same port 80 or 443. The default protocol is HTTP, if user wants to use websockets protocol he/she has to send a upgrade request from HTTP to WS protocol and there is some key authentication and steps.

That's why you need to put them in your nginx configuration.

Refer this if you need more information abouth Protocol upgrade mechanism.

Even though in my opinion, this is not a exact duplicate of this question, I feel obligated to give credits for @Paulo to providing the perfect answer even though is not accepted.

Ad
source: stackoverflow.com
Ad