Socket.io: Error during WebSocket handshake: Unexpected response code: 400

Created on 14 Jan 2015  ·  129Comments  ·  Source: socketio/socket.io

Can't find out a solution, I get this error on the browser console:
WebSocket connection to 'ws://.../socket.io/?EIO=2&transport=websocket&sid=p3af7ZNfvogtq6tAAAG0' failed: Error during WebSocket handshake: Unexpected response code: 400.

Hava any advice ?

Most helpful comment

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

All 129 comments

I am experiencing the exact same issue at the moment, any help?

I am also having this issue since I installed a SSL cert on my domain.

Here is a better description of the issue: http://stackoverflow.com/questions/28025073/error-during-websocket-handshake-unexpected-response-code-400-with-nginx-proxy

Im also having a similar issue connecting with with one of the Android library's. Web sockets won't connect to https through either HAproxy doing ssl termination or letting node do ssl directly. Long polling works fine however

I resolve it by changing the domain to the true IP address:

var socket = io.connect('http://182.92.79.215:3007');

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

Same issue here on production server. Development machines do not show the error.

The strange thing is that the connection is working. I can send messages via WebSockets from the server and the client gets them. I can also see the WebSocket connection being established on the server.

I just get this error in developer tools saying:

WebSocket connection to 'wss://.../socket.io/?EIO=3&transport=websocket&sid=2b_v_BXtbwzl5z2yAAAI' failed: Error during WebSocket handshake: Unexpected response code: 400

I'm using Apache ProxyPass to send connections to node.

Same here - full functional but Error message in dev tools. Somewhere else i read its related to the apache version - using 2.2.14 on this machine.

Make sure you're socket.io connection isn't going through an Amazon Load Balancer. Or if so, do this: http://blog.flux7.com/web-apps-websockets-with-aws-elastic-load-balancing

Same issue here, only in production environment.
Websockets seems to work correctly, the application works without problems. But on console log I can see this error.

I'm using Nginx and only one server for node, so it seems to be not a load balancing problem. I was already using the solution suggested by tylercb (with the exception of "proxy_set_header Host $host;") and it is not solving the issue.

also have problem ,but work well....

I had this same issue. Are you using CloudFlare? Currently, only their Enterprise plan supports WebSockets.

Solved for me. It was due to wrong socket.io address in nginx configuration, that was not matching the path using the websocket.

I googled because I got the same problem and I also use nginx. The solution is to add this part

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;

into the nginx configuration file like tylercb mentioned.

Worked for me. Thanks.

Got same error directly connecting to my app hosted on Windows Server 2008R2 (no proxy, no IIS). Only dumb intermediate hardware in between.

Work fine after switched host name to ip address, i.e. 127.0.0.1:9000. may caused by httpd ProxyPassReverse

In my case, the issue was as a result of cloudfare not supporting websockets on the free plan. I turned off CloudFare for the domain and it worked.

I just needed to add some Apache rewrite conditions to handle the websockets, more info here:
http://stackoverflow.com/a/27534443/2044993

I know this is an old issue, but since it's high on Google search results, but this might help people:

The reason the connection still works even with this error is that socket.io is falling back to AJAX, which is not optimal and you should fix your server configuration.

Btw, this issue should remain closed, it's not a socket.io issue.

@arosenfeld-mentel I keep reading the posts above your comment that "this is not a socket.io issue" but I don't see where anyone says WHAT the issue actually is. I see this myself although, as you say, the connection still seems to work. Any tips would be very gratefully received. Thanks!

The issue could be anything really, you need to debug your whole setup. For me it was NGINX, which as reverse proxy needs the additional configuration settings posted above many times. For you could be something else.

Start by debugging the local connection, get it to work without the warning, then move to the production server and make sure you get firewalls, front-facing servers and proxys to cooperate with WebSockets.

It's not a socket.io issue, but it is a WebSockets issue, so make sure the server and the client works well with WebSockets.

Thanks for the reply. Everything works locally or through our VPN, but once our firewall is involved, the messages appear. I guess that the firewall is interfering somehow and we'll have to learn how to debug that problem instead of blaming socket.io. Thanks again.

Having the exact same issue. I have create stackoverflow (http://stackoverflow.com/questions/34439546/socket-io-with-apache-proxy) question also but things suggested there havent worked for me also yet.

i've put these headers in nginx but still getting the same, but not in browser but in https://toolbox.seositecheckup.com

Had this issue too, looks like my virtual host (through nginx) wasn't set up properly to accept the Upgrade header. @tylercb's fix from about a year ago fixed it^

@tylercb worked for me.

+1
This error occurred on openshift environment

I thought it might be useful if I documented exactly what I did to resolve the issue. This is simply a combination of the various posts above put into a simple post so anybody else finding this issue can get a potential solution easily. I take no credit for the hard work.

We are running

  1. Ubuntu 14.04 LTS Server,
  2. nginx version: nginx/1.4.6 (Ubuntu) as a reverse proxy and SSL gateway.
  3. We do NOT have Apache at all. Its deleted from the system.
  4. We have a node.js server running behind nginx v0.10.25. This listens on port 4001. The external access is via port 4000.
  5. We run ufw on the front and simply allowed port 4000 through.
  6. We manage our own servers and are not using Cloudflare or Openshift
  7. We don't use any load balancer (yet).

We had the same issues as other people of

WebSocket connection to 'ws://XXXXXXXXXX?EIO=2&transport=websocket&sid=p3af7ZNfvogtq6tAAAG0' failed: Error during WebSocket handshake: Unexpected response code: 400.

We updated out nginx file in /etc/nginx/sites-enabled/default to read as follows: (note we've pulled our domain names out)

server {
        listen 4000;
        server_name XXXX.YYYY.ZZZZ;

        root html;
        index index.html index.htm;

        ssl on;
        ssl_certificate /etc/ssl/certs/SSL.crt;
        ssl_certificate_key /etc/ssl/private/server.key;

        ssl_session_timeout 5m;

        # ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2; # NOTE WE REMOVE SSLv3
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES1\
28-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECD\
HE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SH\
A256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SH\
A:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

        ssl_prefer_server_ciphers on;
        ssl_dhparam /etc/ssl/private/dhparams.pem;

        location / {
                 proxy_set_header        Host $host;
                 proxy_set_header        X-Real-IP $remote_addr;
                 proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                 proxy_set_header        X-Forwarded-Proto $scheme;

                 # Fix the “It appears that your reverse proxy set up is broken" error.
                 proxy_pass          http://127.0.0.1:4001;
                 proxy_read_timeout  90;

                 proxy_redirect      http://127.0.0.1:4001 https://XXXXX.YYYYY.ZZZZZ;

                 # These three lines added as per https://github.com/socketio/socket.io/issues/1942 to remove the error
                 # WebSocket connection to 'wss://XXXXX.YYYYY.ZZZZZ:4000/socket.io/?EIO=3&transport=websocket&sid=0hsRiXu0q9p6RHQ8AAAC' failed:\
 Error during WebSocket handshake: Unexpected response code: 400 in console.log

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

We only needed to add in

  1. proxy_http_version 1.1;
  2. proxy_set_header Upgrade $http_upgrade;
    3.proxy_set_header Connection "upgrade";

as we already had

  1. proxy_set_header Host $host;
  2. proxy_pass http://127.0.0.1:4001;

Hope this helps, it worked for us.

Thanks for the help,

Rob

I have the same problem when I authenticate with CAS (not with classic form), and I am using Apache with LetEncrypt SSL as a proxy, is there a specific configuration to set?

Trying to deploy the Node.js app on AWS Elastic BeanStalk.
I've added a the folder .ebextensions to the root of the server, and inside i've added the file nginx.config that looks like this:

server:
  location /:
    proxy_pass http://localhost:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;

I still get the 400 Error, but at least all the rest of the socket works.

For Reference: This is the only working config solution I found for socket.io 1.x and Apache 2.4: https://github.com/meteor/meteor/issues/3339#issuecomment-165188938

ProxyPass / http://localhost:3999/
ProxyPassReverse / http://localhost:3999/

RewriteEngine on
RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
RewriteRule .* ws://localhost:3999%{REQUEST_URI} [P]

using another port, It may work

@BlaM thanks for the working configuration!

For anyone experiencing this problem in AWS Elastic Beanstalk, this .ebextension file worked for me:

files:
    "/etc/nginx/conf.d/01_websockets.conf" :
        mode: "000644"
        owner: root
        group: root
        content : |
            upstream nodejs {
                server 127.0.0.1:8081;
                keepalive 256;
            }

            server {
                listen 8080;

                location / {
                    proxy_pass  http://nodejs;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection "upgrade";
                    proxy_http_version 1.1;
                    proxy_set_header        Host            $host;
                    proxy_set_header        X-Real-IP       $remote_addr;
                    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                }
            }

    "/opt/elasticbeanstalk/hooks/appdeploy/enact/41_remove_eb_nginx_confg.sh":
        mode: "000755"
        owner: root
        group: root
        content : |
            mv /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf.old

As per the instructions in this thread.

@BlaM - I am using an apache server and ELB with Elastic Bean
Where should I put the code you provided?
Thanks!

I have no clue about Elastic Bean, but the code snippet I posted goes into Apache host files.

@tylercb Thanks! it's worked for me~

Anyone not using nginx facing same issue?

Yes I am facing the same issues using Elastic Beanstalk with a single server (node and nginx) without Elastic Load Balancer.
I do not know how to change from http/https to ssl/tls

Same issue with HAProxy here

Worked for me. Thanks. And you should pay attention to the order

proxy_set_header   Upgrade $http_upgrade;
proxy_set_header   Connection "upgrade";

yes this did it for me too.

I am also facing this issue. Even with 400 error, the websocket communication between client and server is fine. This is in our development environment.

Can somebody tell me whether this would be an issue in the production environment.

FYI: connecting to a sails backend using socket.io hosted on Heroku using the postgres addon will throw this error in your browser if your io.sails.url is not https. Very specific case but hopefully this helps anyone with the same stack searching for this on google

Hey Everybody,

I am also facing the same issue on my localhost machine. We are using python-flask as a server and HTML/CSS/JS as a frontend.

In the Frontend to connect with Websocket Server we have used
var socket = io.connect('ws://url/namespace', { 'transports': ['websocket'] });

When I run the application it throws an error
WebSocket connection to 'ws://url/namespace/socket.io/?EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 400

Do tell us if you need more information.

Any Help is appreciated.
Thanks for Reading it.

Regards
Ajay

I got same error on the webrtc sample app. On the server side, the code is

var server = require('http').Server(app);
var io = require('socket.io')(server);
var WebRTC = require('../../');
app.use(express.static(__dirname));
var io = require('socket.io')(server);

After comment out the second "require('socket.io')" the 400 error message goes away.

Need to dig further for why...but you can check if your app tries to make same connection twice.

Hey guys I know this discussion has been going a while and wanted to post a resource that solved this problem for me using AWS EBS with Application Load Balancer.

https://mixmax.com/blog/deploying-meteor-to-elastic-beanstalk-1

Hopes this helps.

Travis

I have same problem when I use etherpad-lite.
I found an approach to fix Apache ProxyPass issue.
I am using Apache http 2.2 uses backprot from http 2.4 wstunnel module so i think that can fix v.2.4 too.

First I have modified socket.io-client/socket.io.js
look for WS.prototype.uri = function()
return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
then change to:

return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path +'ws/'+ query;
save it and restart node.

Second, modify vhost.conf or ssl.conf
add following to VirtualHost:

        ProxyPass "/etherpad/socket.io/ws/" "ws://localhost:9001/socket.io/"
        ProxyPassReverse "/etherpad/socket.io/ws/" "ws://localhost:9001/socket.io/"

        ProxyPass "/etherpad/socket.io/" "http://localhost:9001/socket.io/"
        ProxyPassReverse "/etherpad/socket.io/" "http://localhost:9001/socket.io/"


        ProxyPass /etherpad/ http://localhost:9001/
        ProxyPassReverse /etherpad/ http://localhost:9001/
        CacheDisable *

        ProxyPreserveHost on
        <Proxy *>
                Options FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Proxy>

`

Restart apache httpd, enjoy~

For Apache, follow @cpres answer, it works. My conf

<VirtualHost *:80>
    ServerAdmin [email protected]
    ServerName reservation.tinker.press

    DocumentRoot /var/www/html/node-js-order-socket-demo
    <Directory />
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    RewriteEngine on
    RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
    RewriteCond %{HTTP:CONNECTION} ^Upgrade$ [NC]
    RewriteRule .* ws://127.0.0.1:3001%{REQUEST_URI} [P]

    ProxyRequests Off
    ProxyPreserveHost On
    ProxyVia Full
    <Proxy *>
        Require all granted
    </Proxy>

    ProxyPass / "http://127.0.0.1:3001/"
    ProxyPassReverse / "http://127.0.0.1:3001/"

    ErrorLog ${APACHE_LOG_DIR}/error.log

    # Possible values include: debug, info, notice, warn, error, crit,
    # alert, emerg.
    LogLevel warn

    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

apache-websocket-use-mode-rewrite

I also had to adjust my elastic load balancer to use TCP over port 80 instead of HTTP over port 80.

Is it possible to use ProxyPass directive with several nodes? I ended up using RewriteRule for https://github.com/socketio/socket.io/pull/2819:

Header add Set-Cookie "SERVERID=sticky.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED

<Proxy "balancer://nodes_polling">
    BalancerMember "http://server-john:3000"    route=john
    BalancerMember "http://server-paul:3000"    route=paul
    BalancerMember "http://server-george:3000"  route=george
    BalancerMember "http://server-ringo:3000"   route=ringo
    ProxySet stickysession=SERVERID
</Proxy>

<Proxy "balancer://nodes_ws">
    BalancerMember "ws://server-john:3000"    route=john
    BalancerMember "ws://server-paul:3000"    route=paul
    BalancerMember "ws://server-george:3000"  route=george
    BalancerMember "ws://server-ringo:3000"   route=ringo
    ProxySet stickysession=SERVERID
</Proxy>

RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) balancer://nodes_ws/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) balancer://nodes_polling/$1 [P,L]

@darrachequesne It worked just fine! thanks

Thanks!

For anyone that is still having a problem with AWS Application Load Balancer try updating your Security Policy, I ran into the same problem even though it was working perfectly fine in the exact setup for another website but noticed the security policy was about 1 year behind. Updating it to a newer one seems to have solved the problem.

Having configured nginx and the client i was still struggling to find the exact solution.
But @visibleajay made that comment and i realised that i didn't use the { 'transports': ['websocket'] } part into io.connect command.

So for anyone who might be into the same trouble,
var socket = io.connect('https://server.com/socket.io-path-here', { 'transports': ['websocket'] });

did the job, together with the correct nginx configurations.

@fott1 please be aware that using { 'transports': ['websocket'] } means there's no fallback to long-polling when the websocket connection cannot be established.

Complete example with nginx: https://github.com/socketio/socket.io/tree/master/examples/cluster-nginx

@darrachequesne i believe you are right, what if i include in the array together with 'websocket' the 'polling', or 'xhr-polling'? Thank you for the provided example.

@fott1 the default is indeed ['polling', 'websocket'] (ref).

But you'll need to use the correct nginx configuration, since polling (unlike websocket) transport requires that every request is routed to the same socket.io server.

@darrachequesne in other words. if i have multiple server instances every request should be routed to the same instance? Correct me if i am wrong. Thank you for the tip.

@fott1 yep, that's right. The explanation is here: https://socket.io/docs/using-multiple-nodes/

FWIW - I was getting this on my local (no nginx, no proxies). Turns out with socket.io you have to explicitly specify the transports as first websocket, then polling - in both client and server. No idea why that wouldn't just be the default. Or maybe I'm doing it wrong.

Our original problem was we had polling before websocket, not realizing that order matters.

Original setup:
server:
io.set('transports', ['polling', 'websocket']);
client:
var socket = io.connect(server, { reconnect: true });

We realized our client was polling, so we removed that as an option. Then everything started failing and we got the 400 bad request.

So finally we figured out that you have to specify the order in both client and server, and if you don't the client will just go straight to polling, which makes little sense to me. You'd think it would default to websocket first.

Fix:
server:
io.set('transports', ['websocket', 'polling']);
client:
var socket = io.connect(server, { reconnect: true, transports: ['websocket', 'polling'] });

Again, please be aware that using { 'transports': ['websocket', 'polling'] } means there's no fallback to long-polling when the websocket connection cannot be established.

Let's close that issue, please reopen if needed.

I took the approach of "extending" Elastic Beanstalk's default nginx configuration with the location settings similar to some earlier suggestions. Create a directory structure like so:

~/workspace/my-app/
|-- .ebextensions
|   `-- nginx
|       `-- conf.d
|           `-- myconf.conf
`-- web.jar

where myconf.conf, the name of which is arbitrary so long as it ends in .conf contains the following:

server {
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
    }
}

Be sure to adjust the port number to your needs.

I solved this problem by adding only { transports: ['polling'] } .

_

var socket = io.connect(server, {transports: ['polling']});

_

@hyewon330 By the looks if it you didn't really _solve_ it 😉 You simply configured socket.io to not even try using websockets in the first place. Sure, the error message is gone, but now you're forcing socket.io to use polling even if the connection between client and server _would_ support websockets. Not sure whether that's critical for your application, just wanted to make sure you know 👌

For anyone using Nginx, @tylercb solution works perfectly.

This solution fixed my issue with shiny apps. prefect.

@tylercb @rudolfschmidt @juanjoLenero @rwillett @cpres hello, I use your method but it does not work for me. I doubt if because I use other port rather than 8080. For example, my app is put in machine A with IP 170.8.8.8 monitoring port 5000, and I put nginx in machine B with IP 170.8.8.2 also monitoring port 5000. So I want to visit IP:5000 in B which skip to IP:5000 in A. The below is my nginx config in machine B:

upstream cuitccol.com{ #the name of server cluster
        server 170.8.8.8:5000 max_fails=5 fail_timeout=50s; #for the first web server
        }

    server {
        listen       5000;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            proxy_pass http://cuitccol.com;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
        } 

I do not know where goes wrong. Can you give some advises?
thank you very much~
looking forward to your reply

I'm facing this issue for a while now in every env, including local. @darrachequesne Let me know if I need to provide more info:

I have a node js with express and the code below, which follows exactly the socket.io "How to use" section:

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
io.on('connection', function(){ /* … */ });
server.listen(3000);

And I have set up a very simple client, with just the code below, which follows exactly the socket.io-client "How to use" section:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io('http://localhost:3000');
  socket.on('connect', function(){});
  socket.on('event', function(data){});
  socket.on('disconnect', function(){});
</script>

Despite successfully connecting, I keep getting this same error:

WebSocket connection to 'ws://localhost:3000/socket.io/?EIO=3&transport=websocket&sid=EWl2jAgb5dOGXwScAAAB' failed: Error during WebSocket handshake: Unexpected response code: 400

When looking at the browser console, the error is pointing to the line 112 from websocket.js, which states:

try {
    this.ws = this.usingBrowserWebSocket ? (protocols ? new WebSocket(uri, protocols) : new WebSocket(uri)) : new WebSocket(uri, protocols, opts);
  } catch (err) {
    return this.emit('error', err);
  }

Appreciate any ideas...

@rafapetter Check that you haven't required socket.io twice, I was moving setting up socket.io in www/bin to app.js and accidentally left a require socket.io in the bin which was causing this error.

Just adding {transports: ['websocket']} option to the Socket.io client.

look like this.

import io from 'socket.io-client'
const socket = io('http://localhost:5000', {transports: ['websocket']})

@spookyUnknownUser on the server the socket.io is required only once.

@prapansak the client is a simple javascript file, inside a window.onload function, no ES6 import allowed. But I do follow the How to Use section:
<script src="/socket.io/socket.io.js"></script>

And when calling the socket as you've suggested:
const socket = io('http://localhost:5000', {transports: ['websocket']})

I get this error: WebSocket connection to 'ws://localhost:1337/socket.io/?EIO=3&transport=websocket' failed: Invalid frame header

Thanks guys, if you have any other idea let me know and I'll try it here.

Ok, finally got it solved!

@spookyUnknownUser your idea encourage me to look further for duplications. And as it turns out, on the server right after server.listen I was doing this:

io.attach(server, {
  pingInterval: 40000,
  pingTimeout: 25000,
});

Which in a way I guess is attaching the server a second time. So I removed it and right at the beginning, when requiring socket.io I've changed to:

var io = require('socket.io')(server, {
    'pingInterval': 40000,
    'pingTimeout': 25000
});

Now, no issues are shown, and everything works fine.

Thanks again for the insights guys

I have my socket server on EBS(AWS Elastic beanstalk). I faced similiar issue in the production environment, but could fix the issue without migrating to application load balancer and without changing ngnix or apache configurations.

Changes I made to solve this issue: Instead of https, i allowed ssl on 443 to my application port and opened port 443 on my security group

ebs_lb
ebs_sg

For those on AWS: Use the Application Load Balancer and make sure you have stickiness enabled on the Target Group.

Turns out this was happening intermittently on our server when it was overloaded and within peak usage time.

Allowing the default settings while io.connect fires off creates XHR requests and a WS request shortly after (as mentioned above). All these XHR requests were overloading the server and thus returning the 400 errors. By using the suggestions above of adding transports: ['websocket']} to the client code the issue has been resolved. Will post an update if the 400 errors persist but it seems solid for now.

have the same issue, neither suggested above server side changes resolved it. On the client side I have socket = io.connect(); in react module and it's able to figure out what server I'm connecting to.
Providing server parameter to connect is kind of a hassle but without it how can I specify { reconnect: true, transports: ['websocket', 'polling'] }? Sounds like an optional parameter is #1 but the crucial one is #2

i added this below, and it worked for me.
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

I unfortunately have the same problem, even with the good headers set .. I created a stackoverflow with my config..

https://stackoverflow.com/questions/50431587/proxy-socket-io-fails-to-connect-with-nginx-node-on-docker

Make note of the double quotes here:

proxy_set_header Connection "upgrade";

I received this message using single quotes but using doubles quotes resolved it.

After about an hour of troubleshooting, it seems like there might be some additional configuration needed if you're running your endpoint as a cluster.

see here

tylercb solution worked for me (NgInx). Thanks!

The key for us was the proxy_http_version 1.1; in @tylercb's solution

I have edited NginX config on my server according those many comments.
But it works partially. At first, after server restart, it works bad. Mostly with hard-refresh. Later it works better, just sometimes need hard refresh.
Locally with sails lift it works
But if i am using locally sails lift --prod then i get the same thing, that socket cannot connect and 400 bad response. But after some tries it is again stable.

This seems still not solved - no final solution found why we have this..

I added the various configurations in the documentation: https://socket.io/docs/using-multiple-nodes/

Any suggestion for improvement is welcome! => https://github.com/socketio/socket.io-website

I just face this problem and still got an error even disable my nginx. Finally the problem because of express-status-monitor middleware on express, this makes HTTP call on the first (request) handshake of WebSocket goes failed.

I try to disable that middleware and the WebSocket working well

There are several reasons why you would get 400 during the handshake. Following are few things that could be tried

  1. Enable 443 in (ufw/Other) firewalls as well as settings of (AWS/Google cloud/Other) console
  2. Do not enable TLS in nodejs server. Do it through nginx.
  3. Use following nginx config
    Note: This only works if your socket.io connection is already working but using long polling instead of websockets. Add below code and it will start using websocket.
        location /{
            proxy_pass http://127.0.0.1:5000/;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $host;
        }

Maybe it will be help to some one. I got this error when created two instances of socket.io in my app. I called io = socketio(server) twise and got error 400. That is some sort of stupid errors but... that was mine.

Make sure you enable Sticky sessions in your application load balancer (don't use classic load balancer). If you don't use sticky sessions, you'll probably get the 400 errors randomly.

This is because when the upgrade from polling to websocket is attempted, it’s possible that the load balancer balances the upgrade request to a server that never encountered this socket id and throws the 400 error. More details here: https://socket.io/docs/using-multiple-nodes/

Also, if you're using elastic beanstalk, add the following websocket.config file in the .ebextensions folder to support Nginx upgrading to Websockets:

container_commands:
  enable_websockets:
    command: |
     sed -i '/\s*proxy_set_header\s*Connection/c \
              proxy_set_header Upgrade $http_upgrade;\
              proxy_set_header Connection "upgrade";\
      ' /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf

I googled because I got the same problem and I also use nginx. The solution is to add this part

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;

into the nginx configuration file like tylercb mentioned.

So at least this works when you use nginx as reverse proxy, here is the explanation

WebSocket proxying
To turn a connection between a client and server from HTTP/1.1 into WebSocket, the protocol switch mechanism available in HTTP/1.1 is used.

There is one subtlety however: since the “Upgrade” is a hop-by-hop header, it is not passed from a client to proxied server. With forward proxying, clients may use the CONNECT method to circumvent this issue. This does not work with reverse proxying however, since clients are not aware of any proxy servers, and special processing on a proxy server is required.

Since version 1.3.13, nginx implements special mode of operation that allows setting up a tunnel between a client and proxied server if the proxied server returned a response with the code 101 (Switching Protocols), and the client asked for a protocol switch via the “Upgrade” header in a request.

As noted above, hop-by-hop headers including “Upgrade” and “Connection” are not passed from a client to proxied server, therefore in order for the proxied server to know about the client’s intention to switch a protocol to WebSocket, these headers have to be passed explicitly:

location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}
A more sophisticated example in which a value of the “Connection” header field in a request to the proxied server depends on the presence of the “Upgrade” field in the client request header:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    server {
        ...

        location /chat/ {
            proxy_pass http://backend;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
        }
    }
By default, the connection will be closed if the proxied server does not transmit any data within 60 seconds. This timeout can be increased with the proxy_read_timeout directive. Alternatively, the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive.

Any news on this issue? I referenced it here https://stackoverflow.com/questions/49575350/websocket-connection-to-wss-error-during-websocket-handshake-unexpected-re

Many users have this problem because of the nginx configuration and while on dedicated servers you can fix it using the ideas above (nginx configuration, etc) many users don't have access to these settings, so it would be great to have some kind of fix from sockets.io on this...

Nobody?

@deemeetree
If you use multiple nodes which seems to me possible due the error message on your site "Session ID unknown", you should take a look to my answer on StackOverflow (https://stackoverflow.com/a/53163917/6271092).

To be short, socket.io as they say on their site (https://socket.io/docs/using-multiple-nodes/),

If you plan to distribute the load of connections among different processes or machines, you have to make sure that requests associated with a particular session id connect to the process that originated them.

This is due to certain transports like XHR Polling or JSONP Polling relying on firing several requests during the lifetime of the “socket”. Failing to enable sticky balancing will result in the dreaded:

Error during WebSocket handshake: Unexpected response code: 400

So you cannot have multiple sockets that work on different nodes. Reconfigure your server as it says. And for your Express, configure your port like this,
parseInt(your_port) + parseInt(process.env.NODE_APP_INSTANCE);

Hope it helps.

Got same error directly connecting to my app hosted on Windows Server 2008R2 (no proxy, no IIS). Only dumb intermediate hardware in between.

I am facing same issue on Window server 2016 please mention your fix. Thanks

I have been facing this issue for quite some time. If anyone has any information please help. I am using apache server.

Any clue? Works great on dev but same issue on SSL.

Using PM2 Node Cluster mode: 4 x 1

Using apache proxy, still no clue

For those who are on httpd2/apache2, I have a solution that appears to work:

    ProxyRequests Off
    <Location />
        ProxyPass http://127.0.0.1:1337/
        ProxyPassReverse http://127.0.0.1:1337/

        RewriteEngine On
        RewriteCond %{HTTP:UPGRADE} ^WebSocket$ [NC]
        RewriteCond %{HTTP:CONNECTION} Upgrade$ [NC]
        RewriteRule /socket.io/(.*) ws://127.0.0.1:1337/socket.io/$1 [P]
    </Location>

Hey @ZeldaZach is that for the .htaccess file? thanks!

This config is in my sites-enabled/site.conf file, but it should work in htaccess too afaik

All i can say, SSL is culprit here :) disable, cloudflare flexible SSL mode and move to FULL(STRICT) mode. Sure this will fix the issue,
If not,

Use localhost:port in reverse proxy configuration.

For anyone here using Nginx and React Express (MERN). I had the same issue because I only the the proxy_pass http://localhost:3000; line. I added the following lines based on @tylercb answer from above in this thread:

"Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/ "

If anyone still having problems using Nodejs + Express, maybe your problem could be express-status-monitor, as @slaveofcode mentioned. As sated in its NPM documentation, this module spawn its own socket.io instance, so you should fill websocket parameter with your main socket.io instance, as well port parameter:

const io = require('socket.io')(server);
const expressStatusMonitor = require('express-status-monitor');
app.use(expressStatusMonitor({
  websocket: io,
  port: app.get('port')
}));

This is my Apache config, notice it's using a /ws/ path prefix, but otherwise it works fine.

    ProxyRequests Off
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>

    RewriteEngine On
    RewriteCond %{REQUEST_URI}  ^/ws/socket.io         [NC]
    RewriteCond %{QUERY_STRING} transport=websocket    [NC]
    RewriteRule /ws/(.*)           ws://localhost:6001/$1 [P,L]
    ProxyPass /ws http://127.0.0.1:6001
    ProxyPassReverse /ws http://127.0.0.1:6001

Currently facing this issue with exposing the Linkerd dashboard (service mesh) for our EKS cluster. we use nginx, so not exactly sure how to get out of that one at this moment.

@andrzj OMG man, you just saved me.

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

it works.

Thanks for the help gang! If anyone is trying to get this working next to normal HTTPS traffic, it's now working on Elastic Beanstalk for me with the following settings:

  • On Elastic Beanstalk: nginx turned off (for now, haven't got round to trying the above config yet).
  • On Elastic Beanstalk: Load balancer as follows:
  • On Node/Express: const io = require('socket.io')(3030)
  • On client simply naming let connection = io(https://www.myurl.com:3030)

If you guys are still having this issue and you have set allowed origin in socket server as array or origins instead of callback function to filter origins would throw this error

Error during WebSocket handshake: Unexpected response code: 400

In my case updating this logic

io.origins(['https://foo.example.com:443']);

to this

io.origins((origin, callback) => {  
     if (origin !== 'https://foo.example.com') {    
         return callback('origin not allowed', false);  
     }  
    callback(null, true);
});

Worked without any errors thrown.

Getting this same error, but I added in the configs for NGINX and I am still receiving the same 400 handshake error. I am however, using an Application Load Balancer in AWS and I have it set to an :80 Target Group and a 443 listener which forwards to the Target Group.

NGINX conf file:

`server {

listen [::]:80;
listen 80;

server_name <domain_name>;
access_log  /var/log/nginx/access.log;

location / {
    proxy_pass http://127.0.0.1:8000;
    include proxy_params;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

location /socket.io {
    include proxy_params;
    proxy_http_version 1.1;
    proxy_cache_bypass $http_upgrade;
    proxy_buffering off;
    proxy_set_header Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_pass http://127.0.0.1:8000;
}

}`

And within my js file I have a connection for socket.io that looks like this:
var socket = io()

Create manual instance (without express app instance) and assign a different port

const io = require('socket.io')(3001, {
  path: '/',
  serveClient: false,
  // below are engine.IO options
  pingInterval: 10000,
  pingTimeout: 5000,
  cookie: false
})

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

I was missing proxy_set_header Connection "upgrade";

I've been spending a whole night to solve this problem when I start to use https or wss or ssl. It always says connection stopped before establish with 400 error code.

Just a minutes ago, I found a solution for that:

0. Cloudflare

At the SSL/TLS tab:

  • If you have your own cert or SSL or HTTPS: set it to Full. (The following 123 steps assume you have your own https certification)

  • If you only have an http server: set it to Flexible. (The Cloudflare will add https or ssl to your website automatically.)

  • After that, go to DNS tab, set Proxied.

If you are not sure what you are doing, just go to DNS tab, set DNS only

1. Make sure you have a right proxy configuration.

server {
    listen 80;
    server_name ai-tools-online.xyz;
    return 301 https://ai-tools-online.xyz$request_uri;
}

server {
    listen 443 ssl http2;

    ssl_certificate       /data/v2ray.crt;
    ssl_certificate_key   /data/v2ray.key;
    ssl_protocols         TLSv1.2 TLSv1.3;
    #ssl_ciphers           3DES:RSA+3DES:!MD5;
    server_name ai-tools-online.xyz;

    location / {
        proxy_pass http://127.0.0.1:5000;
    }

    location /socket.io {
        proxy_http_version 1.1;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://127.0.0.1:5000/socket.io;
    }
}

ai-tools-online.xyz is your domain, http://127.0.0.1:5000 is your socket server.

2. Make sure your server Cross-Origin Controls is set to '*' to allow Cross-Origin Access

For flask-socketio, is to use flask_socketio.SocketIO(app, cors_allowed_origins = '*')

3. You must restart the nginx to let the new config work

systemctl restart nginx

4. For more details about how to set caddy, see the following links:

https://github.com/yingshaoxo/Web-Math-Chat#reverse-proxy-configuration-for-https
https://caddy.community/t/using-caddy-0-9-1-with-socket-io-and-flask-socket-io/508/6
https://www.nginx.com/blog/nginx-nodejs-websockets-socketio/

I googled because I got the same problem and I also use nginx. The solution is to add this part

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;

into the nginx configuration file like tylercb mentioned.

worked for me well thank you !

If you are using Elastic Beanstalk just like me to create node-server,
While creating the environment we are being asked in configurations to use which Proxy Server. In which nginx is pre-populated or default set.
I set that proxy server to none and then continued to continue creating my server. I was using Elastic Beanstalk to create a node server in which my proxy server was default set to nginx.
As it is an error of configuring proxy server. After removing any proxy server, the error disappeared.

Been googling for hours and none of the solutions above applied to us since we just had a nodejs app and no nginx.

The way we solved this was just to disable nginx from the container -> load balancer settings to pass all traffic directly to node.

Been googling for hours and none of the solutions above applied to us since we just had a nodejs app and no nginx.

The way we solved this was just to disable nginx from the container -> load balancer settings to pass all traffic directly to node.

how did you do that?

If you go to Configuration > Load balancer you can find a drop-down for the proxy server, you can use nginx, Apache or set it to "none" to pass through all connections to the node app.

This only appears if you create an environment with a load balancer, doesn't work for single instances

Edit: my original comment was referred to Elastic Beanstalk

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

So so thank youuu.

This document is for those who use laravel-echo-server & Nginx & socket.io & Redis-server with the separated server between client project and Redis-server.

Please follow the link here.

Thanks

I had this issue. Updating my nginx config didn't help, but @santhosh77h 's solution fixed it for me. For some reason passing the array of allowed origins doesn't work, but using the callback does.

I use Nest.js websockets (just a wrapper around Socket.io) and added the following to my gateway:

afterInit(server: Server): any {
    const origins = getOrigins(); // returns an array of origin strings
    server.origins((origin, cb) => {
      if (origins.includes(origin)) {
        cb(null, true)
      } else {
        cb('Invalid origin', false);
      }
    });
  }

I had the same issue with NUXT.js with Node.js / Express running on AWS Elastic Beanstalk (Nginx proxy). Took me a few days to figure this out. I'll share my reading points. Maybe someone will find it useful.

My environment is on Application Load Balancer with two ports 80 for https and 443 for https with SSL.

In the combination of the answer from above, big thanks to @tylercb and official documentation from AWS and socket.io documentation I created an Nginx config file that seems to be fixing the issue.

I will quickly outline the steps:

In my index.js Node file:

const express = require('express')
const app = express()
const server = http.createServer(app)
const io = require('socket.io')(server)
const host = process.env.HOST || '127.0.0.1'
const port = process.env.PORT || 8081

On the front-end (one of my components):
import io from 'socket.io-client';
in my Vue data():
socket: io()

Finally, In the application root, I created a folder .ebextensions
Right inside I created a file 01-proxy.config with the following content:

files:
  /etc/nginx/conf.d/01-proxy.conf:
     mode: "000644"
     owner: root
     group: root
     content: |
        upstream nodejs {
          server 127.0.0.1:8081;
          keepalive 256;
        }
        server {
          listen 8080;
          server_name yourdomain.com;

          if ($time_iso8601 ~ "^(\d{4})-(\d{2})-(\d{2})T(\d{2})") {
            set $year $1;
            set $month $2;
            set $day $3;
            set $hour $4;
          }
          access_log /var/log/nginx/healthd/application.log.$year-$month-$day-$hour healthd;
          access_log  /var/log/nginx/access.log  main;

          location / {
              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
              proxy_set_header Host $host;

              proxy_pass http://nodejs;

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

          gzip on;
          gzip_comp_level 4;
          gzip_types text/html text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

          location /static {
              alias /var/app/current/static;
          }

        }

  /opt/elasticbeanstalk/hooks/configdeploy/post/99_kill_default_nginx.sh:
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/bin/bash -xe
      rm -f /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf
      service nginx stop 
      service nginx start

container_commands:
  removeconfig:
    command: "rm -f /tmp/deployment/config/#etc#nginx#conf.d#00_elastic_beanstalk_proxy.conf /etc/nginx/conf.d/00_elastic_beanstalk_proxy.conf"

Additional readings:
nginx configuration

That's it. Quite lengthy. My apologies and good luck.

working for me below change in ubuntu and ngnix server for angular .net core

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

If anyone still having problems using Nodejs + Express, maybe your problem could be express-status-monitor, as @slaveofcode mentioned. As sated in its NPM documentation, this module spawn its own socket.io instance, so you should fill websocket parameter with your main socket.io instance, as well port parameter:

const io = require('socket.io')(server);
const expressStatusMonitor = require('express-status-monitor');
app.use(expressStatusMonitor({
  websocket: io,
  port: app.get('port')
}));

This info helped me

Make sure you're socket.io connection isn't going through an Amazon Load Balancer. Or if so, do this: http://blog.flux7.com/web-apps-websockets-with-aws-elastic-load-balancing

If someone else had this issue using AWS load balancer, The article metioned doesn't say that it's possible too to use SSL as load balancer protocol and keep using your certificate on this configuration, out of you app server level.

This is how my LB listeners looks like

image

Worked well for me!

Had the same issue, my app is behind nginx. Making these changes to my Nginx config removed the error.

location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}

This is originally from https://chrislea.com/2013/02/23/proxying-websockets-with-nginx/

Yes. This was help-ful and worked for me as well.

Make sure you're socket.io connection isn't going through an Amazon Load Balancer. Or if so, do this: http://blog.flux7.com/web-apps-websockets-with-aws-elastic-load-balancing

If someone else had this issue using AWS load balancer, The article metioned doesn't say that it's possible too to use SSL as load balancer protocol and keep using your certificate on this configuration, out of you app server level.

This is how my LB listeners looks like

image

Worked well for me!

Nice, it worked.

Make sure you're socket.io connection isn't going through an Amazon Load Balancer. Or if so, do this: http://blog.flux7.com/web-apps-websockets-with-aws-elastic-load-balancing

If someone else had this issue using AWS load balancer, The article metioned doesn't say that it's possible too to use SSL as load balancer protocol and keep using your certificate on this configuration, out of you app server level.

This is how my LB listeners looks like

image

Worked well for me!

in this case your application is running on 80?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Elliot9 picture Elliot9  ·  4Comments

adammw picture adammw  ·  4Comments

gCurtisCT picture gCurtisCT  ·  4Comments

jloa picture jloa  ·  4Comments

thebinarypenguin picture thebinarypenguin  ·  4Comments