Nginx – Socket.io with Sails.js/Node.js and NGINX on SSL: bad gateway

nginxnode.jssocket.iossl

I just started venturing in NGINX and SSL.

Using Ubuntu 16.04.

I am running a Sails server on the standard 1337 port and just set up NGINX with SSL (using letsencrypt). Port 80 is redirected to 443 and upstream goes to Sails.

I also have a Tomcat server listening on 8080 and use NGINX to redirect the same way.

Everything works fine: I can browse both servers on https without special ports on browser.

I have set up socket.io to use websockets protocol only (no polling). This is set on the server and on the browser client.

However, socket.io (sails.io) throws a 502 error no the browser. (polling gave an error too)

Here is my NGINX sites-available for the Sails server:

upstream sails {
   server 127.0.0.1:1337 fail_timeout=0;
}

server {
   listen 80;
   listen [::]:80;
   server_name mysails.server.com;
   return 301 https://$server_name$request_uri;
}

server {
   listen 443;
   listen [::]:443 ssl http2;
   server_name mysails.server.com;
   include snippets/ssl-mysails.server.conf;
   include snippers/ssl-params.conf;
   large_client_header_buffers 8 32k;

   location / {
      proxy_pass http://sails/;
      proxy_http_version 1.1;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header Port $server_port;
      proxy_set_header X-Real-IP $remot_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-NginX-Proxy true;
      proxy_pass_request_headers on;

   }

   location /socket.io/ {
      proxy_pass http://sails/;
      proxy_http_version 1.1;
      proxy_redirect off;
      proxy_set_header Host $host;
      proxy_set_header Port $server_port;
      proxy_set_header X-Real-IP $remot_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-NginX-Proxy true;
      proxy_pass_request_headers on;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_buffers 8 32k;
      proxy_buffer_size 64k;

   }   
}

The snippets/ssl-mysails.server.conf and snippers/ssl-params.conf files contain:

ssl_certificate /path/to/letsencrypt/fullchain.pem;
ssl_certificate_key /path/to/letsencrypt/privkey.pem;

and

# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# Disable preloading HSTS for now.  You can use the commented out header line that includes
# the "preload" directive if you understand the implications.
#add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

Anyone has any clue on what's going on…?

UPDATE ERROR LOG and odd behaviour

My nginx error log says this:

upstream prematurely closed connection while reading response header from upstream, client: XXXXXXXX, server: mysals.server.com, request: "GET /socket.io/?__sails_io_sdk_version=0.13.8&__sails_io_sdk_platform=browser&__sails_io_sdk_language=javascript&EIO=3&transport=websocket HTTP/1.1", upstream: "http://127.0.0.1:1337/?__sails_io_sdk_version=0.13.8&__sails_io_sdk_platform=browser&__sails_io_sdk_language=javascript&EIO=3&transport=websocket", host: "mysails.server.com"

And my Sails verbose log is inconsistent. It might say this:

*verbose*: Rendering view: "homepage" (located @ "/opt/FillForm/views/homepage")
*verbose*: • using configured layout:: layout (located @ "/opt/FillForm/views/layout")
*verbose*: Rendering view: "homepage" (located @ "/opt/FillForm/views/homepage")
*verbose*: • using configured layout:: layout (located @ "/opt/FillForm/views/layout")
*verbose*: Rendering view: "fileLink" (located @ "/opt/FillForm/views/fileLink")
*verbose*: • using configured layout:: layout (located @ "/opt/FillForm/views/layout")
*verbose*: Sending 404 ("Not Found") response
*verbose*: Sending 404 ("Not Found") response
*verbose*: Rendering view: "fileLink" (located @ "/opt/FillForm/views/fileLink")
*verbose*: • using configured layout:: layout (located @ "/opt/FillForm/views/layout")
*verbose*: Sending 404 ("Not Found") response
*verbose*: Sending 404 ("Not Found") response

Which is strange, because there should be only one request to the /opt/FillForm/views/fileLink view/page.

If you refresh the page (F5), the Javascript console on the browser might just say:

 Socket is trying to reconnect to Sails...
_-|>_-  (attempt #7)

and every attempt results on a series of

verbose: Rendering view: "homepage" (located @ "/opt/FillForm/views/homepage")
verbose: • using configured layout:: layout (located @ "/opt/FillForm/views/layout")

messages on the Sails verbose log. Why are socket requests causing Sails to render the homepage view?

If you refresh with ignore cache (shift+F5), the Javascript console begins showing the attempt AND the 502 bad gateway message:

WebSocket connection to 'wss://mysqils.server.com/socket.io/?__sails_io_sdk_version=0.13.8&__sails_io_s…tform=browser&__sails_io_sdk_language=javascript&EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 502
        Socket is trying to reconnect to Sails...
_-|>_-  (attempt #20)

And the Sails verbose log just shows two instances of the following message:

verbose: Sending 404 ("Not Found") response
verbose: Sending 404 ("Not Found") response

And it stops showing any other errors (not like on the previous scenario, where every websocket attempt resulted on view rendering messages.)

In this case, it seems the wss requests are not getting through Nginx to Sails.

Best Answer

So the issue is a bad configuration on the NGINX sites-avalable conf file.

location /socket.io/ {
      proxy_pass http://sails/;
...
}

should be

location /socket.io/ {
      proxy_pass http://sails/socket.io/;
...
}

Pretty basic stuff: the "location" is not forwarded to the proxy_pass (why would it be, right?) -- So you need to make sure the socket requests are redirected to the exact socket endpoint.

Related Topic