Proxying WebSockets with Nginx and Socket.IO

Why would you want to proxy WebSockets through Nginx?

That’s probably the first question that comes to mind. Why would you want to proxy your websockets?

The answer is pretty simple. You want your Nginx to run on port 80 (duh) but you also want your WebSockets to go through port 80, right?
That way, you shouldn’t have any trouble connecting to a WebSocket server when you are behind a firewall.

The Problem

My problem was that my WebSocket server was running on port 5000. It would work when connecting to it from my localhost.

ws://localhost:5000

But when I wanted to push it to production and try it on my university’s network it wouldn’t work!
Obviously that was to be expected.

Our university’s network only allows a couple of ports through their firewall. Obviously port 80 was one of them and port 5000 was not.

Now the problem is that my server was already running Nginx on port 80. So obviously my WebSocket server couldn’t run on port 80.
And this is where proxying comes in play.

Using WebSocket proxying in Nginx I can keep my WebSocket server running on port 5000 and my Nginx on port 80.

All I had to do was configure my Nginx to start proxying WebSockets from port 80 (external) to port 5000 (internal).

After that, you can simply connect to

ws://yourhost.com:80 (You don’t have to specify the port if you’re using port 80, but I’m doing this anyway just to make it clear)

Let’s start doing stuff already!

Ok, so the first thing you need to know is that WebSocket proying is supported in Nginx since version 1.3.13 which is (as of this date) a development version.
So if you’re running Nginx in production, you might not want to upgrade just yet.

The first thing you want to do is open up the Nginx configuration file. I’m using nano (no VIM, sorry) for this.
Then find where you defined your virtual server and add the configuration needed to proxy websockets:

location /socket.io/ {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}

So, what exactly is happening here?

First, we define the route in which we want to work. When using Socket.io as the WebSocket server, all WebSocket traffic will normally go through the /socket.io/ route. e.g. http://michieldemey.be/socket.io/
(You can change this default route in socket.io’s configuration: https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO)

Anyway, my Nginx is running on port 80 (external) and I need to proxy all WebSocket traffic to port 8080 (internal).
We can specify this using “proxy_pass” setting.

As you can see in my configuration above, I’m proxying everything to http://localhost:8080. So now I’m proxying all WebSockets to my SebSocket server running on port 8080.

But that’s not all. You need to specify that you want to use HTTP 1.1, because WebSockets use the “Upgrade” header that’s only available in HTTP 1.1 and not HTTP 1.0.

The other two settings are basically just to fill in the Upgrade and Connection header. Leave it as is and it should work.

And that’s pretty much it!

So now I’m proxying WebSockets through port 80 to port 8080. And that without too much configuration.
For more information, check out Nginx’s official page on this topic: http://nginx.org/en/docs/http/websocket.html

Michiel De Mey

Full-time geek, Full Stack Engineer and Full Metal Hero. NodeJs, AngularJs, API design, WebSockets, WebSec & IoT enthusiast. Former San Francisco resident.

More Posts - Website - Twitter - Facebook - LinkedIn - Google Plus