"Curiosity is the very basis of education and if you tell me that curiosity killed the cat, I say only the cat died nobly." - Arnold Edinborough

Version 1.3.13 of nginx is out and with it comes support for Connection: upgrade and Upgrade header, meaning proxying of WebSockets! Many people have been waiting for this and “are websockets in nginx supported?” is one of the most frequent questions in #nginx on freenode. With that out of the way, time to have a look at the nginx WebSocket implementation.

The New Nginx Websockets Configuration Directives

The documentation provided states the configuration as follows:

location /chat/ {
    proxy_pass ​http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

This is indeed fairly simple and configuring the HTTP version used isn’t even new at all. We can go ahead and improve the versatility a bit by creating a connection variable thus allowing us to move these proxy_set_headers into a generic included file.

map $http_connection $upgrade_requested { 
    default upgrade;
    ''      close;
}

This makes the variable $upgrade_requested available to proxy_set_header Connection and if connection upgrade wasn’t requested then it will default back to “” to not interfere with normal requests. The idea here being that if you always proxy HTTP/1.1 then you don’t need a location specifically to handle WebSockets.

Connection upgrading seems unlikely to be back ported into the stable branch so if you wish to use this you will have to use the development branch. Thankfully development in nginx does not mean that it’s not run-time stable, it just means that the API might change and as such it only affects module authors. Don’t be afraid to install the development version to play around with this new feature.

Limitations

So far a few limitations are known in the websocket implementation.

Client Must Specify Connection Upgrade
The client need to have requested the connection upgrade, otherwise nginx will fail. Currently that part is listed as a todo in the code so I can’t say how it will fail, but I’m sure it will be implemented eventually so don’t rely on it. This limitation should not be an issue to anyone and the only issue with it is a theoretical situation where a module author might want to use connection upgrade to a backend and then process the response itself.

WebSockets Time Out
WebSockets are still affected by proxy_read_timeout which defaults to 60 seconds. This means that if you have an application using WebSockets but not sending any data more than once per 60 seconds you either need to increase the timeout or implement a ping message to keep the connection alive. The ping solution has the added benefit of discovering if the connection was closed unexpectedly.

Keep-Alive & WebSockets
Keep-alive pings aren’t usable for working around the above mentioned timeout as they work on the TCP level are are just empty packets. They aren’t reported to the application and thus the application will never respond to them meaning that proxy_read_timeout will still trigger.

WebSockets Supports SSL
Since WebSockets tie into the normal proxy module SSL works the exact same way it normally would.

Proxy Buffers
WebSockets utilize two memory buffers the size of proxy_buffer_size, one for upstream data and another for downstream data. The difference between WebSockets and a normal proxy request is that WebSockets will never buffer to disk.

Case Sensitive Upgrade Header
Some backends do a case sensitive check on the upgrade header and will require the header to be either “upgrade” or “Upgrade”, failing on one and working on the other. If things seem to be right but doesn’t work then try changing the case of the header.

  • Newman

    Posted: February 22, 2013


    "The client need to have requested the connection upgrade, otherwise nginx will fail. Currently that part is listed as a todo in the code so I can’t say how it will fail, but I’m sure it will be implemented eventually so don’t rely on it."

    This is an incorrect interpretation of commentary in the code: "TODO: prevent upgrade if not requested or not possible". This comment is from a function that handles the response from backend. So, it actually means that currently it is possible for backend to upgrade protocol even if nobody has requested. Reply


    • Martin Fjordvald

      Posted: February 22, 2013


      I actually based it on the quote "Connection upgrade is allowed as long as it was requested by a client via the Upgrade request header." from http://trac.nginx.org/nginx/changeset/5073/nginx. I did just briefly glance at the todo and assumed it wasn't yet implemented, though, it might already be. Do you interpret the commit message differently? Reply


      • Newman

        Posted: February 23, 2013


        "Connection upgrade is allowed as long as it was requested by a client via the Upgrade request header."
        This is actually how WebSocket works according to RFC 6455. Reply


        • Martin Fjordvald

          Posted: February 23, 2013


          Yeah but my point is that there might not be a way for nginx to act as a client itself. There are 3rd party modules that add extensive logic to nginx which make it more of an application server. This limitation might affect modules ability to interact with WebSockets for whatever reason they might want to. I specifically said that this isn't relevant to most people but I still think it's worth mentioning. Reply


  • Mr C

    Posted: February 26, 2013


    One thing I've noticed, nginx ram usage, which used to be stable, now continually increases. Restarting nginx doesn't solve the issue. but restarting the node.js websocket server returns the ram to its usual level.

    Could be something I have misconfigured elsewhere. Could be dodgy stats reporting. Reply


  • Phaitour

    Posted: February 27, 2013


    In certain version of websocket implementation, the headers for Upgrade and Connection are case-sensitive and have to match "Upgrade: WebSocket\r\nConnection: Upgrade\r\n".

    So I've started using:

    proxy_set_header Upgrade "WebSocket";
    proxy_set_header Connection "Upgrade"; Reply


    • Phaitour

      Posted: February 27, 2013


      Sorry, I posted too soon. This included some of my own debug code. It appears the only line that's needs to be case-sensitive is the Connection: Upgrade line. So these two pairs of proxy_set_header's work:

      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade"; Reply


  • Parker Parsons

    Posted: April 26, 2013


    Would that be the correct way to proxy standard HTTP as well as websockets? I don't want the Upgrade header or Connection being set to "upgrade" unless that's what the browser sent, but these proxy_set_header lines are required for websockets to work. Why doesn't nginx just forward the original Upgrade/Connection headers? Reply


    • Martin Fjordvald

      Posted: April 27, 2013


      If you use the map to create the header then it will be fine. You have to set these lines because nginx does not forward anything, nginx is a terminating reverse proxy, not a transparent pass-through proxy. Reply



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>