Posts Tagged ‘web sockets’

Web sockets with Socket.io, Node.js, and Nginx: port 80 considered harmful

TL;DR: web sockets are more widely supported on port 443 (via SSL) than port 80. Check that your ISP actually supports the port you’re using at WebSocketsTest.com.

I just wasted a good half-day trying to debug a problem with web sockets in the Socket.io plugin for Node.js running on Nginx 1.4.1.

Running the Node app locally on port 3000, everything worked swell. However, as soon as I deployed it to my production server, I was seeing about a 10-second startup in my app. This occurred in four different browsers – Chrome, Chromium, Firefox, and Safari.

Each one was reporting a 502 Bad Gateway response from the server. So since the client assumed web sockets were not being supported, Socket.io was falling back on XHR polling, which is less speedy.

The Node logs said:

warn: websocket connection invalid
info: transport end (undefined)

The Nginx logs said:

2013/05/21 10:41:00 [error] 6117#0: *8 upstream prematurely closed 
    connection while reading response header from upstream [...]

Neither was very helpful. So after reading several forums and blog posts online, and adding lots of tweaks and hacks that didn’t work, my Nginx site configuration bloated up to this:

# node.js running locally on port 3000
upstream node {
    server 127.0.0.1:3000;
    keepalive 256; # not necessary
}


# the public nginx server instance running on port 80
server {
    listen 80;
    server_name <mysite.com>; # domain of my site
    access_log /var/log/nginx/<mysite>.access.log;
    error_log /var/log/nginx/<mysite>.error.log;
    
    # supposedly prevents 502 bad gateway error;
    # ultimately not necessary in my case
    large_client_header_buffers 8 32k;

    # run the app on the root directory
    location / {

        # the following is required for WebSockets
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
 
        # supposedly prevents 502 bad gateway error;
        # ultimately not necessary in my case
        proxy_buffers 8 32k;
        proxy_buffer_size 64k;
        
        # the following is required
        proxy_pass http://node;
        proxy_redirect off;
 
        # the following is required as well for WebSockets
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        tcp_nodelay on; # not necessary
    }
 }

Since web sockets worked fine when running Node locally, I assumed it was a problem with Nginx. What eventually clued me in was that even running Node directly on port 80 yielded the same error.

Finally I realized that the problem was with my Internet provider themselves – they were disallowing web sockets connections on port 80! WebSocketsTest.com turned out to be an invaluable resource in debugging this.

Incidentally, it would have worked if I had used SSL on port 443. According to WebSocketsTest’s aggregate data, port 443 is supported about 89% of the time, compared to 78% for port 80.

I tested a few public Wifi spots around Geneva and can confirm that this figure seems about right. I would be curious to know if support varies by geographic region, but WebSocketsTest doesn’t publish that data.

Lesson #1: even if you’re using a modern browser, your Internet provider may not be up to snuff. Check it first, before you wreck your brain!

Lesson #2: If you plan on using web sockets in a production environment, SSL is apparently the way to go. Not only is it secure, but it’s also better supported (as of 2013).