Nginx и Websockets

    В Nginx наконец добавили долгожданную функциональность по проксированию Websockets.
    В связи с этим спешу поделиться конфигами и небольшими подробностями.

    Для тех кто не знал, ранее проксирование было возможно через сторонний модуль, с ограничениями которые многих не устраивали. Ну, да мы не об этом.


    Теперь на одном порту можно проксировать http и ws траффик, более того, например под одним SSL сертификатом, и все это с родным знакомым синтаксисом.

    Ws доступны начиная с версии 1.3.13, а буквально сегодня добавили поддержку еще в модули ngx_http_uwsgi_module и ngx_http_scgi_module для 1.3.14

    Вот, что говорит документация на счет используемого механизма.


    Для превращения соединения между клиентом и сервером из HTTP/1.1 в WebSocket используется доступный в HTTP/1.1 механизм смены протокола.

    Но есть сложность: поскольку “Upgrade” является hop-by-hop заголовком, то он не передаётся от клиента к проксируемому серверу. При прямом проксировании клиенты могут использовать метод CONNECT, чтобы обойти эту проблему. Однако при обратном проксировании такой подход не работает, так как клиент ничего о проксирующем сервере не знает, и требуется специальная обработка на проксирующем сервере.

    Начиная с версии 1.3.13, в nginx предусмотрен особый режим работы, который позволяет установить туннель между клиентом и проксируемым сервером, если проксируемый сервер вернул ответ с кодом 101 (Switching Protocols), и клиент попросил сменить протокол с помощью заголовка “Upgrade” в запросе.

    Как уже отмечалось выше, hop-by-hop заголовки, включая “Upgrade” и “Connection”, не передаются от клиента к проксируемому серверу, поэтому, для того чтобы проксируемый сервер узнал о намерении клиента сменить протокол на WebSocket, эти заголовки следует передать явно:

    Конфиг.


    Простой пример:

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


    Более сложный пример, в котором значение поля “Connection” в заголовке запроса к проксируемому серверу зависит от наличия поля “Upgrade” в заголовке запроса клиента:

    http {
        map $http_upgrade $connection_upgrade {
            default upgrade;
            ''      close;
        }
    
        server {
            ...
    
            location /ws/ {
                proxy_pass http://localhost:8080;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
            }
        }
    


    И немного неочевидный, но важный параметр это proxy_read_timeout, который стоит по дефолту в значении 60s, по истечении которых коннект обрывается, чего в случае ws обычно совсем не нужно.

    Поэтому, мы добавили:

    http {
          ...
          proxy_read_timeout 950s;
          ...
    }
    
    

    Скорее всего вашему приложению нужны будут другие цифры таймаута, поэтому не копипастите бездумно;)
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 12

      +22
      На официальном сайте есть руководство.
      И не забываем читать документацию по директивам:
        +2
        Спасибо Валентин, как то пропустил на русском ссылку. Поправил.
        0
        Наконец-то! Год ждал :) Надоели тысячи костылей с проверками открыт ли у пользователя нестандартный порт.
          0
          Хм, и что если открыт?
            0
            Смысл в том, что нужно было использовать вебсокеты. А т.к. стоит nginx на 80 порту а проксировать вебсокеты он не умеет, нужно было использовать нестандартный порт чтобы общаться с node.js и socket.io и их вебсокетами. А т.к. нестандартные порты отрублены у достаточного числа людей — нужно скатываться на jsonp в этом случае, который уже можно через proxy_pass.

            А порты ещё эти когда закрыты — бывает — просто подвисают, когда пытается с ними соединится, а бывает сразу выдают что закрыты. Вообщем теперь всё это позади! :)
              0
              А можно было вебсокеты повесить так же на 80 порт, но на дополнительный IP адрес.
                0
                Это если есть дополнительный IP, но чаще его просто нет:)
                  0
                  на локейшн же
                    0
                    2013 год же:) Прочитайте внимательно коммент выше!
          –1
          Отличная новость, но… почему нельзя сделать чтобы всё просто работало, без лишних настроек, без установки дополнительных заголовков и map-ов?
            0
            чтобы скучно не было и была работа у всех :)
              +2
              Вчера вечером поднял очень быстро, даже без какого-то особого опыта работы с nginx. Скомпилил, настроил, запустил, спроксировал, посмотрел и выключил.

              Заработало как надо.

            Only users with full accounts can post comments. Log in, please.