Nginx: HTTP/2 не работает

Настройка HTTP/2 в Nginx может показаться тривиальной задачей, однако есть ряд потенциальных проблем, с которыми вы можете столкнуться. В данной статье я опишу несколько из тех, с которыми столкнулся лично я при настройке наших серверов.

Настройка HTTP/2 в Nginx

Для включения HTTP/2 в Nginx достаточно добавить пару строк в конфигурацию вашего сервера:

  1. server {
  2. listen 443 ssl http2;
  3. ssl_certificate <ПУТЬ К *.crt ФАЙЛУ>;
  4. ssl_certificate_key <ПУТЬ К *.key ФАЙЛУ>;
  5. #…
  6. }

Указываем 443 порт, т.к. он является портом по умолчанию для SSL, а также, что сервер будет использовать SSL и работать с HTTP/2. Еще необходимо указать пути к файлам сертификатам. Ничего сложного.

Проблема 1: Invalid parameter «http2»

Если вы добавили все указанные выше настройки, перезагрузили Nginx и получили такую ошибку, то первое, что вам необходимо сделать — проверить версию Nginx:

  1. nginx v
  2. nginx version: nginx/1.11.1

Как я описал ранее в одной из статей, Как настроить HTTP/2 с Varnish используя Nginx, Nginx должен быть версии не ниже 1.9.5, а также скомпилирован вместе с модулем http2. В противном случае строка

  1. listen 443 ssl http2;

будет приводить к критической ошибке.

Проблема 2: Nginx по прежнему отдает данные используя HTTP/1.1 (в Chrome)

Вы можете сразу же перейти к решению, или же ознакомиться со всем более детально и понять причину и суть проблемы.

Хорошо, у вас уже установлен Nginx правильной версии вместе с http2 модулем, но как проверить все ли настроено верно и отдает ли Nginx данные используя именно HTTP/2?

Приведу несколько способов:

HTTP/2 Тест — онлайн сервис

Наиболее простым способом является проверка через инструмент http2 тест. Достаточно просто ввести домен вашего сайта и вы узнаете результаты:

Обратите внимание на секцию ALPN. В ней должно быть указано, что поддержка ALPN включена, иначе HTTP/2 не будет работать в Chrome.

Проверка HTTP/2 с помощью расширения для Chrome

Данный способ хорош тем, что вы будете постоянно видеть поддерживает тот или иной сайт HTTP/2. Вы можете установить расширение HTTP/2 and SPDY indicator. Оно добавляет индикатор справа от строки поиска, который становится синим если страница работает через HTTP/2.

Проверка с помощью Chrome Dev Tools

Еще одним способом является дополнительная настройка в Chrome Dev Tools. Откройте панель отладки, выберите вкладку Сеть, далее вызовите контекстное меню нажав на заголовок любой из колонок. В появившемся меню отметьте Протокол. Это все, теперь у вас есть дополнительная колонка, которая отображает протокол, с помощью которого были загружены те или иные ресурсы. Просто перезагрузите страницу у увидите что-то вроде:

Здесь мы видим, что ресурсы отдаются через HTTP/2.

Почему HTTP/2 может не работать в Chrome

В одной из статей Chrome (оригинал) было указано, что:

… начиная с 15 мая 2016 года — годовщины спецификации по HTTP/2 — Chrome более не будет поддерживать SPDY. Серверы, которые не будут поддерживать  HTTP/2 к тому времени будут обслуживать запросы от Chrome через HTTP/1.1.

В то же время, Chrome перестанет поддерживать TLS расширение NPN, которое позволяет серверам использовать SPDY и HTTP/2 соединения с клиентами. NPN заменен новым расширением для TLS — ALPN, опубликованым IETF в 2014. ALPN уже используется в 99% процентах случаев HTTP/2 и Chrome, а остальные серверы могут настроить поддержку ALPN обновив их SSL библиотеки.

Обновление: чтобы лучше понять цикл обновлений в Chrome, SPDY и NPN поддержка будет удалена с выходом Crhome 51.

Это значит, что если ваш веб сервер не поддерживает ALPN расширение для TLS, то пользователи Chrome будут по прежнему получать данные через HTTP/1.1 и не увидят никаких улучшений в производительности.

SPDY

SPDY — это протокол, изначально разработанный Google, для передачи и транспортировки веб данных. Он управляет HTTP трафиком с конкретными целями по уменьшению сетевых задержек и улучшению веб безопасности. SPDY достигает уменьшения сетевых задержек за счет сжатия данных, мультиплексирования и приоритизации обращений. В ходе разработки, основные разработчики SPDY были вовлечены в разработку HTTP/2. В феврале 2015 Google объявил, что следует стандарту HTTP/2 и поддержка SPDY будет упразднена в 2016 году.

NPN

NPN (Next Protocol Negotiation) — это предшественник ALPN, который широко использовался вместе с SPDY. NPN похож на ALPN, однако главным отличием является то, что он не включает список поддерживаемых протоколов в своем ClientHello сообщении. Само расширение NPN включено в ClientHello сообщение, однако именно сервер отправляет клиенту список протоколов, из которых клиент может сделать выбор. Подразумевается, что на клиенте есть собственный список протоколов (в порядке их приоритета), которые он поддерживает и что он выберет один из тех протоколов, которые поддерживаются сервером.

ALPN

Application-Layer Protocol Negotiation (ALPN) — это еще одно расширение для TLS (Transport Layer Security) для выбора протокола. ALPN позволяет выбрать протокол, который должен будет использоваться при работе через защищенное соединение. При этом он позволяет избежать дополнительных сетевых обращений. Данное расширение используется протоколом HTTP/2.

Когда множество протоколов поддерживается на одном сервере и одном порту, таком как 443, клиент и сервер должны договориться о том, какой из протоколов они будут использовать при каждом соединении. Это соглашение должно быть достигнуто без дополнительных сетевых запросов между клиентом и сервером, т.к. каждый из них ухудшает производительность и опыт клиента.

TLS рукопожатие

С ALPN, именно клиент отправляет список поддерживаемых им протоколов как часть TLS ClientHello сообщения. Далее сервер выбирает протокол и отправляет уже выбранный протокол клиенту как часть TLS ServerHello сообщения. Таким образом выбор протокола может быть достигнут в рамках TLS рукопожатия и без необходимости дополнительных запросов.

Подразумевается, что на сервере есть список протоколов, которые он поддерживает (с их приоритетами) и что сервер выберет один из тех, которые также поддерживаются и клиентом.

Ниже приведу список SSL библиотек и их минимальные версии, в которых присутствует поддержка ALPN (wiki):

  • GnuTLS начиная с версии 3.2.0, выпущенной в мае 2013;
  • MatrixSSL с версии 3.7.1, выпущенной в декабре 2014;
  • Network Security Services с версии 3.15.5, выпущенной в апреле 2014;
  • OpenSSL с версии 1.0.2, выпущенной в январе 2015;
  • LibreSSL с версии 2.1.3, выпущенной в январе 2015;
  • mbed TLS (ранее PolarSSL) с версии 1.3.6, выпущенной в апреле 2014;
  • SChannel с версии 8.1 / 2012 R2;
  • s2n начиная с релиза в июне 2015;
  • wolfSSL (ранее CyaSSL) с версии 3.7.0, выпущенной в октябре 2015.

Т.к. мы будем компилировать Nginx с OpenSSL, то нам потребуется версия не ниже 1.0.2.

Решение: Nginx с OpenSSL 1.0.2 (ALPN) на CentOS 7

Если вы используете OpenSSL, вам необходимо будет скомпилировать Nginx с более новой версией этой библиотеки (смотрите точные версии в ALPN/SSL списке). Мы используем CentOS 7 и для данной ОС уже есть Gist, который позволяет произвести все необходимые обновления:

  1. yum y groupinstall ‘Development Tools’
  2. yum y install wget openssldevel libxml2devel libxsltdevel gddevel perlExtUtilsEmbed GeoIPdevel
  3.  
  4. OPENSSL=«openssl-1.0.2h»
  5. NGINX=«nginx-1.11.1-1»
  6.  
  7. mkdir p /opt/lib
  8. wget https://www.openssl.org/source/$OPENSSL.tar.gz O /opt/lib/$OPENSSL.tar.gz
  9. tar zxvf /opt/lib/$OPENSSL.tar.gz C /opt/lib
  10.  
  11. rpm ivh http://nginx.org/packages/mainline/centos/7/SRPMS/$NGINX.el7.ngx.src.rpm
  12. sed i «s|—with-http_ssl_module|—with-http_ssl_module —with-openssl=/opt/lib/$OPENSSL|g» /root/rpmbuild/SPECS/nginx.spec
  13. rpmbuild ba /root/rpmbuild/SPECS/nginx.spec
  14. rpm Uvh /root/rpmbuild/RPMS/x86_64/$NGINX.el7.centos.ngx.x86_64.rpm

Выводы

В моем случае я случайно заметил, что Nginx по прежнему отдавал данные через HTTP/1.1 даже после того, как был настроен HTTP/2. Именно поэтому полезно знать о возможных проблемах когда вы впервые настраиваете что-то новое.