Наверняка вы слышали о новых версиях HTTP — второй и третьей. Что за этими версиями? Зачем они были разработаны? Чем они отличаются от классического HTTP/1.1 и где могут быть полезны? Какие настройки предоставляет веб‑сервер Angie для этих протоколов? Все эти вопросы мы будем разбирать в этой статье.
Начнём с краткой истории развития протокола HTTP.
Навигация по циклу
Настройка location в Angie. Разделение динамических и статических запросов.
Перенаправления в Angie: return, rewrite и примеры их применения.
Сжатие текста в Angie: статика, динамика, производительность.
HTTP/2 и HTTP/3: настройка, достоинства и недостатки.
Видеоверсия
Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.
Первые версии HTTP
Протокол HTTP (HyperText Transport Protocol) появился как средство передачи текстовых данных при зарождении WWW (World Wide Web) — веба.
Первоначальная версия HTTP/0.9 (1991) была максимально проста: однострочный протокол, отсутствие заголовков запроса и ответа, только один тип контента (текст).
Следующая версия HTTP/1.0 (1996) получила поддержку заголовков, кодов ответа, указание версии и другие возможности. Но эта версия была переходной, фичи добавлялись по мере развития браузеров и серверов, то есть не было строгого стандарта.
Версия HTTP/1.1 (1997) принесла дополнительные возможности, но при этом еще и стандарт, которому могут следовать веб‑серверы и клиенты. Важнейшие фичи этой версии:
chunked encoding (поблочное кодирование/передача);
управление кэшированием через заголовки;
согласование контента;
обязательное использование заголовка Host;
дополнительные методы (PUT, PATCH, DELETE, CONNECT, TRACE, OPTIONS).
Именно HTTP/1.1 до сих пор является полноценной по возможностям версией, которая удовлетворяет потребностям большинства веб‑приложений. То есть, с учетом работы с TLS (HTTPS), мы можем запустить практически любой сайт на HTTP/1.1 и всё будет работать. Возникает закономерный вопрос: зачем разработали следующие версии: 2 и 3? Короткий ответ: для ускорения загрузки сайтов. Основной проблемой HTTP/1.1 стало отсутствие мультиплексирования запросов (в стандарте была поддержка конвейеризации (pipelining), но она не получила широкого распространения в софте). Чтобы обойти это ограничение, браузеры открывали множество параллельных TCP‑подключений к серверу и загружали контент в несколько потоков. Подробнее возможности новых версий (2 и 3) мы рассмотрим ниже.
HTTP/2
Протокол HTTP/2 (2015) возник на базе SPDY/3.1, разработки Google. HTTP/2 привнёс несколько новых возможностей:
мультиплексирование запросов;
сжатие заголовков (HPACK);
приоритезация запросов;
возможность переиспользования соединений (connection reuse);
внутреннее бинарное представление;
Server push.
При внедрении появилось фактическое требование использования TLS (обозначение H2), хотя в стандарте предусмотрена открытая версия протокола (без шифрования — H2C).
Новые возможности выглядели впечатляющими, энтузиасты искали применение этим фичам и замеряли эффект от их применения. Работа по оптимизации скорости загрузки сайтов упростилась: теперь можно было (даже нужно) отказаться от доменного шардинга (разделения ресурсов сайта по нескольким доменам), не так критичным стало объединение ресурсов и встраивание (inline).
Но не все фичи HTTP/2 работали, как задумано. Например, Server Push так и не удалось довести до промышленного применения, так как он вступал в конфликт с идей кэширования ресурсов и мог приводить к излишней нагрузке на канал. Приоритезация ресурсов также скорее не работала, прежде всего из‑за проблемы Head of line blocking (блокировка начала очереди) в транспортном уровне TCP. Кроме того, стандартный TCP (c алгоритмом контроля перегрузки cubic) крайне чувствителен к потери пакетов на линии. HTTP/2 с единственным TCP‑подключением стал более уязвимым в этом смысле по сравнению с HTTP/1.1, где браузер открывал до 6 параллельных соединений к серверу. Одной из попыток решить эту проблему стал алгоритм TCP BBR, но он в свою очередь породил дополнительные проблемы для других участников сети.
Внедрение нового прокола в веб‑серверы также шло непросто. Сначала поддержка HTTP/2 появилась в mainline‑версии Nginx, регулярно исправлялись ошибки реализации и проблемы работы протокола (в том числе, уязвимости). Такие проблемы обусловлены прежде всего высокой сложностью протокола и необходимостью тестирования совместимости с различными клиентами (прежде всего, браузерами). Сейчас HTTP/2 в Angie имеет статус стабильного протокола, пригоден для использования в боевой среде.
Как обычно, для решения проблем HTTP/2 была предложена следующая версия протокола: HTTP/3.
HTTP/3
Как и его предшественник, HTTP/3 (2022) был основан на протоколе, разработанном компанией Google (QUIC), имеет короткое обозначение h3. Впервые в истории HTTP был изменён транспортный протокол: c TCP на UDP. Таким образом, функции управления потоком и обеспечение надёжности передачи перешли из ядра ОС на уровень приложений. Новый транспортный протокол на основе UDP назвали QUIC, поверх него уже работает HTTP/3. В целом изменений по сравнению с HTTP/2 было немного:
переход на транспорт QUIC;
обязательное встроенное шифрование TLS;
более полное шифрование данных при передаче;
установление подключения без задержек (0-RTT handshake);
возможность миграции соединений.
Переход на UDP‑транспорт открыл возможность более тонкого контроля трафика на уровне приложений и потенциально более простого обновления протокола. Ну и конечно, UDP позволяет более эффективно бороться с проблемой блокировки начала очереди (HOL blocking). Без проблем также не обошлось: использование UDP и нового транспорта приводит к накладным расходам по вычислительным ресурсам сервера. Всё таки традиционно в ОС и сетевых устройствах оптимизировался именно TCP как основной транспорт тяжелого трафика.
Возможность миграции соединений позволяет при переключении сети сохранять информацию о подключении, особенно полезна для мобильных устройств.
Судя по статистике Cloudflare, основным протоколом для доставки контента сайтов является HTTP/2 (доля около 60%), использование HTTP/3 пока меньше (доля около 30%).
Поддержка протоколов HTTP/2 и HTTP/3 на момент написания статьи довольно высока, то же касается и поддержки в популярных браузерах. Если вы не знакомы с настройкой HTTPS в Angie, рекомендую ознакомиться со статьёй по этой теме. На этом логично приступить к настройке новых версий HTTP в веб‑сервере Angie.
Настройка HTTP/2
Перед включением HTTP/2 стоит вспомнить о механизме ALPN (Application-Layer Protocol Negotiation), благодаря которому клиент может договориться с сервером о поддержке HTTP/2 и начать его использование. Во время TLS-рукопожатия происходит обмен данными о возможностях клиента и сервера, одной из которых будет как раз поддержка протокола HTTP/2 (h2). Механизм ALPN поддерживается в библиотеке OpenSSL c версии 1.0.2. Скорее всего, в вашей системе более свежая версия этой библиотеки, но помнить об этом стоит.
Итак, поддержка HTTP/2 не требует подключения каких-либо модулей, достаточно добавить директиву http2:
server { listen 443 ssl; http2 on; ... }
На этом настройку можно завершить. Останется проверить работу протокола, например с помощью средств разработчика браузера или просмотра логов сервера.
Однако, модуль позволяет настроить некоторые дополнительные параметры, которые влияют на работу протокола.
Первая настройка это количество параллельных потоков (streams) в соединении HTTP/2. По умолчанию имеет значение 128, что будет достаточно для подавляющего большинства случаев. Манипулировать этим числом можно в исследовательских целях, например, чтобы оценить влияние на скорость загрузки или установить более жёсткие ограничения для защиты от DoS‑атак. Здесь стоит заметить еще одно неприятное отличие работы H2/H3 от HTTP/1.1: новые протоколы создают только одно соединение на клиента и оно будет обрабатываться в одном рабочем процессе Angie. Старый HTTP/1.1, используя несколько соединений, разделяет нагрузку на несколько рабочих процессов, даже для одного клиента. Настройка по умолчанию (допустима в контекстах http и server):
http { http2_max_concurrent_streams 128; }
Вторая настройка отвечает за внутренний размер частей, на которые разделяется тело ответа (chunks). По умолчанию составляет 8 KB. При увеличении этой настройки можно снизить накладные расходы, но это будет негативно влиять на механизм приоритезации запросов. Здесь еще стоит упомянуть, что существует буферизация на уровне TLS (директива ssl_buffer_size, по умолчанию 16 KB) и её также стоит учитывать в процессе настройки. Настройка размера частей ответа:
http { http2_chunk_size 8k; }
Также можно регулировать различные размеры буферов для тела запроса (http2_body_preread_size) и рабочего процесса (http2_recv_buffer_size). В модуле можно найти несколько настроек для механизма Server Push, но рассматривать их нет смысла, потому что он поддержка Server Push удалена из кода сервера. На этом можно перейти к конфигурации протокола HTTP/3.
Настройка HTTP/3
Начнём с требований к библиотеке SSL. Для использования протокола HTTP/3 необходима поддержка TLS 1.3, то есть версия OpenSSL не ниже 1.1.1. Если вы планируете использовать 0-RTT рукопожатия, то версия OpenSSL должна быть от 3.5.1 и выше. Также допустимо использование других библиотек.
Реализация протокола HTTP/3 в Nginx начиналась как отдельная ветка экспериментального кода и только позже получила статус стабильной. Реализация в Angie имеет значительные отличия от Nginx. Прежде всего это использование механизма BPF (подробнее в статье от разработчиков) и возможность использования HTTP/3 при подключении к апстримам.
В отличие от HTTP/2, третья версия протокола требует большей подготовки окружения. Во‑первых, H3 работает поверх UDP, а это значит, что необходимо удостовериться в отсутствии блокировок на уровне сетевых фильтров порта UDP/443. Во‑вторых, переключение на HTTP/3 потребует создания отдельного соединения, поэтому механизм согласования ALPN здесь нам не поможет. Отсюда можно сделать вывод, что не лишним будет включить HTTP/2 как запасной вариант для сервера. Принципиально есть два метода сообщить клиенту о поддержке HTTP/3 на сервере: заголовок Alt‑Svc и HTTPS‑записи в DNS, их можно применять как по отдельности, так и все сразу. Для начала разберём общие настройки HTTP/3.
quic_bpf on; http { quic_gso on; quic_retry on; ssl_early_data on; http3_max_concurrent_streams 128; ... }
Директива quic_bpf (указывается только в контексте main) позволяет выполнять маршрутизацию пакетов QUIC при помощи eBPF, требует версию ядра Linux не ниже 5.7, подробнее об этом можно узнать из статьи от разработчиков.
Внутри контекста http с помощью директивы quic_gso мы включили использование оптимизации GSO (Generic Segmentation Offloading) в Linux, что позволит снизить нагрузку на процессор сервера. Эта оптимизация должна поддерживаться сетевой картой и быть активной. Проверить это можно с помощью ethtool.
# ethtool -k enp4s0 | grep generic tx-checksum-ip-generic: off [fixed] generic-segmentation-offload: on generic-receive-offload: on
Следующая директива quic_retry включает механизм валидации адреса клиента для предотвращения амплификационных атак (QUIC Address Validation). Далее разрешаем использовать 0-RTT рукопожатие (ssl_early_data on), здесь нужно учитывать, что приложение должно быть подготовлено к возможным replay‑атакам.
Наконец, мы видим уже знакомую нам из HTTP/2 директиву http3_max_concurrent_streams. Она имеет тот же смысл, что и для HTTP/2 и также настроена по умолчанию на достаточное количество потоков. Это были общие настройки Angie, теперь можно рассмотреть конфигурацию сайта с использованием HTTP/3.
server { listen 443 ssl; listen 443 quic reuseport; http2 on; ... add_header Alt-Svc 'h3=":443"; ma=86400'; }
В этом примере мы используем одновременно работу HTTP/2 и HTTP/3. Обратите внимание на директиву listen 443 quic reuseport. Она открывает сокет с портом UDP/443 и опцией reuseport (её можно использовать только один раз в конфигурации сервера для каждого сокета).
Для переключения на HTTP/3 мы анонсируем его поддержку в заголовке ответа Alt-Svc. При этом указываем, что протокол доступен на 443 порту (UDP) и разрешаем кэшировать этот заголовок на сутки. Применять такой заголовок стоит осторожно (после тестирования), потому что при отключении HTTP/3 на сервере клиенты, получившие ранее такой заголовок будут пытаться подключиться сразу на UDP/443 и будут получать ошибку. Сессия при первом подключении будет начинаться с HTTP/2 подключения (первые запросы), далее клиент получит заголовок и в случае успешного подключения перейдёт на использование HTTP/3.
Дополнительно мы можем анонсировать поддержку HTTP/3 с помощью HTTPS‑записи в DNS. Например, такой:
example.com 3600 IN HTTPS 1 . alpn="h3,h2"
В этой записи мы указали поддержку HTTP/2 и HTTP/3 для домена example.com. Современные браузеры до подключения к серверу отправляют запрос на наличие HTTPS‑записи. Однако, даже наличие записи не гарантирует работу по HTTP/3 с первого запроса. То есть, клиент может подключиться по HTTP/2 и первые запросы будут использовать эту версию, но далее браузер установит подключение по HTTP/3 и следующие запросы будут уже по HTTP/3.
Как мы говорили выше, HTTP/3 предназначен для общения клиента и сервера, но в Angie также реализована поддержка HTTP/3 для проксируемых серверов. Посмотрим, как можно использовать эту фичу.
HTTP/3 к апстриму
Для использования HTTP/3 при подключении к проксируемому серверу достаточно указать версию протокола при подключении. Также логично использовать keepalive-подключения для более эффективного использования соединения. Примерная конфигурация будет выглядеть так:
upstream backend { server 127.0.0.1:8080; keepalive 16; } server { ... location / { proxy_pass https://backend; proxy_http_version 3; proxy_ssl_name $host; proxy_ssl_server_name on; proxy_set_header Connection ""; proxy_ssl_protocols TLSv1.3; ... } }
Основная директива, которая включает HTTP/3 в сторону апстрима: proxy_http_version 3. Как и в основном модуле HTTP/3 можно регулировать количество параллельных потоков (proxy_http3_max_concurrent_streams) и размер буфера для чтения и записи QUIC (proxy_http3_stream_buffer_size).
Для подавляющего большинства приложений использование HTTP/1.1 при подключении к апстримам с keepalive будет оптимальным решением. Использовать H3 можно в тех редких случаях, когда бэкенд не поддерживает HTTP/1.1 или нужно использовать именно UDP‑протокол для исходящих соединений.
Какой эффект можно получить от новых версий HTTP?
Мы уже рассмотрели возможности новых версий HTTP, они были направлены на ускорение загрузки сайтов. Но насколько мы можем ускориться, просто переключив протоколы?
Разделим ответ на две части. Первый аспект — производительность веб‑сервера. С этой точки зрения новые протоколы сложнее в обработке, включают сжатие заголовков, требуют сохранения состояния и поэтому приводят к деградации производительности (по различным данным на 20–30%). При этом есть и положительный эффект: снижение количества соединений в сторону клиентов. При использовании HTTP/2 мы получаем одно TCP‑соединение от клиента вместо шести в HTTP/1.1. Для HTTP/3 это будет одно UDP‑соединение. Кроме того, сокращается количество дорогих операций TLS‑рукопожатий.
Второй аспект — скорость загрузки сайтов браузером в реальных сетевых условиях. Здесь новые протоколы реализуют свой потенциал и действительно дают ускорение. В большинстве случаев мы можем рассчитывать на улучшение метрик начала рендеринга страниц. Не стоит ждать радикального улучшения, но практически всегда положительный эффект будет. Основным источником улучшений будет именно возможности мультиплексирования запросов, то есть отправка запросов без ожидания ответов и снятие ограничения на 6 параллельных потоков.
Для объективного сравнения текущего состояния протоколов были проведены многочисленные тесты на примере пяти реальных сайтов с применением системы WebPageTest. Условия тестирования: актуальная версия браузера Chrome, сетевой профиль 3GFast (задержки 150 мс, ширина канала: 1,6 Мбит/с download), 10 повторов для каждой страницы. Типичный результат сравнения по временным метрикам показан на картинке ниже.

Как видно из картинки, большинство метрик выигрывают от использования HTTP/2 и HTTP/3 при этом обычно HTTP/3 лучше, чем HTTP/2. Также заметно, что нет радикального улучшения практически по всем метрикам (кроме Load Time (onload), но это не очень стабильная метрика). Также и нет обратной ситуации, где новые протоколы дают заметную деградацию скорости. В качестве исключения можно вспомнить ситуацию (описана в статье про TLS) с Kernel TLS и HTTP/2, где HTTP/2 сильно проигрывал на сценарии отдачи больших файлов через SSL_sendfile(). Также стоит оговориться, что при тестировании на канале с потерей пакетов HTTP/2 может выглядеть значительно хуже других версий.
Это те результаты, которые можно ожидать для среднего сайта из реального мира. Возможно, разница между HTTP/1.1 и новыми версиями стала больше, если вообще не применять методики оптимизации под старый протокол. Но за десятилетия существования HTTP/1.1 настолько укоренился, что практически везде можно встретить объединение ресурсов, которое снижает количество запросов и соответственно эффект от мультиплексирования запросов. Влияние сжатия заголовков может быть заметно только в случаях большого объёма заголовков (cookie) в общем трафике. Если говорить о трафике в целом, то H2/H3 добавят около 4–6% к общему объёму по сравнению с HTTP/1.1 — это накладные расходы на работу новых протоколов.
Отдельно можно сказать про работу приоритезации запросов: она действительно будет лучше работать в HTTP/3. Заметить этот эффект можно на страницах с большим количеством изображений на странице. Там браузер может выставить корректный приоритет и протокол доставит первыми картинки, которые находятся на первом экране.
Итоги
Новые версии протокола HTTP уже сегодня активно используются и приносят пользу для пользователей. Наверняка, их потенциал не используется полностью, но совершенствование софта как с клиентской (браузеры), так и серверной (веб‑сервера, балансировщики) не останавливается. Мы рассмотрели эволюцию протокола HTTP и научилить конфигурировать использование HTTP/2 и HTTP/3 в сервере Angie. Использовать или нет новые версии для ваших проектов — решение индивидуальное, но знать их особенности всегда полезно.
