Что такое HTTP — текстовый протокол поверх TCP
HTTP (Hypertext Transfer Protocol) — это правила, по которым ваш браузер, ваш Python-скрипт и ваш мобильный телефон общаются с серверами в интернете. Когда вы открываете github.com, происходит около двадцати HTTP-запросов: за HTML, за CSS, за JS, за картинками, за API-ответами. Когда ваш ETL-скрипт тянет данные из API партнёра, он тоже говорит HTTP.
В этом уроке разберём, что такое HTTP на самом деле: какой это протокол, как он лежит поверх TCP, чем отличаются версии 1.1, 2 и 3, и почему «stateless» — это не баг, а главная архитектурная фича.
История: от документов к API
HTTP родился в 1991 году в CERN. Тим Бернерс-Ли проектировал «World Wide Web» — систему гипертекстовых документов, в которой можно ссылаться с одного документа на другой. Нужен был протокол передачи этих документов. Так появился HTTP/0.9 — однострочный протокол, который умел только одно: вернуть HTML по запросу.
Ключевой момент: HTTP/1.1 — это до сих пор основа большинства интернета. По данным Cloudflare на 2026 год, около 55% запросов в мире идут по HTTP/2, около 30% — по HTTP/3, и около 15% — всё ещё HTTP/1.1. При этом семантика везде одинаковая: методы, статус-коды, заголовки. Версия меняет только то, как байты передаются по проводам. Поэтому, изучив HTTP/1.1, вы понимаете 95% всего, что нужно для работы.
HTTP — это текстовый протокол (в версии 1.1)
В HTTP/1.1 запросы и ответы — это обычный ASCII-текст, который можно прочитать глазами. Это огромное преимущество для отладки: вы открываете curl -v, видите буквально, что улетело на сервер, что вернулось — и без специальных тулзов понимаете происходящее.
Вот как выглядит самый простой HTTP-запрос:
GET /get HTTP/1.1
Host: httpbin.org
Accept: */*
User-Agent: curl/8.4.0
И ответ от сервера:
HTTP/1.1 200 OK
Date: Thu, 14 May 2026 10:30:45 GMT
Content-Type: application/json
Content-Length: 312
{
"args": {},
"headers": {
"Host": "httpbin.org",
"User-Agent": "curl/8.4.0"
},
"url": "https://httpbin.org/get"
}
Заметьте структуру: первая строка (request-line или status-line), потом заголовки в формате Имя: Значение по одному на строку, потом пустая строка, потом опционально тело. Это и есть HTTP — никакой бинарщины, никаких магических констант.
В HTTP/2 и HTTP/3 формат бинарный — для производительности. Но семантика та же: методы, статус-коды, заголовки. И что важно: curl -v всё равно покажет вам всё в текстовом виде, потому что библиотека сама декодирует бинарный поток обратно в текст для отладки.
HTTP лежит поверх TCP (в большинстве случаев)
HTTP — это протокол прикладного уровня (Layer 7 в OSI). Он не передаёт байты по сети сам — он использует для этого транспортный протокол. В версиях HTTP/1.1 и HTTP/2 — это TCP. В HTTP/3 — QUIC, который под капотом UDP.
Модель OSI: зачем нужны слои TCP 3-way handshake: как устанавливается соединениеЧто делает TCP под капотом, упрощённо:
- 3-way handshake. Клиент шлёт SYN, сервер отвечает SYN-ACK, клиент шлёт ACK. После этого соединение установлено и можно передавать данные. Это занимает 1 RTT (round-trip time) — типично 30-100 мс.
- Reliable delivery. TCP гарантирует, что байты дойдут в том порядке, в котором были отправлены. Если пакет потерялся — TCP сам перешлёт.
- Flow control. Если получатель не успевает обрабатывать данные — TCP замедлит отправителя.
- Congestion control. Если в сети загруженность — TCP уменьшит скорость отправки.
Для HTTP это значит: отправил запрос — гарантированно получишь ответ (если сервер вообще ответит). Не нужно проверять контрольные суммы и переотправлять — TCP делает это сам.
# Посмотреть TCP-соединения вашей машины:
# macOS/Linux:
netstat -an | grep ESTABLISHED | head
# или (более современный):
lsof -i -P -n | grep ESTABLISHED | head
# Это покажет открытые TCP-соединения, в том числе HTTP-сессии браузера и приложений
HTTPS — это HTTP внутри TLS
Вы видите в адресной строке браузера https:// — это HTTP внутри TLS (Transport Layer Security). TLS добавляет три вещи:
- Шифрование. Никто между клиентом и сервером не прочитает содержимое запроса.
- Аутентификация сервера. Сертификат подтверждает, что вы реально говорите с
github.com, а не с подменённым сервером. - Целостность. Никто не может изменить запрос или ответ по дороге незаметно.
Деталям TLS посвящён модуль 3. Пока важно понимать: HTTPS — это не «другой протокол», это HTTP, обёрнутый в TLS-туннель. Семантика, методы, статус-коды — всё то же самое.
# Сравните вывод этих двух команд -- вы увидите тот же HTTP-обмен,
# только во втором случае с TLS-handshake перед HTTP:
curl -v http://httpbin.org/get 2>&1 | head -20
curl -v https://httpbin.org/get 2>&1 | head -30
HTTP/2: мультиплексирование и бинарный формат
HTTP/1.1 имеет одно неудобное свойство: на одно TCP-соединение в одно время можно отправить ровно один запрос и ждать ответа. Если страница хочет загрузить 50 ресурсов (CSS, JS, картинки), браузер открывает несколько TCP-соединений параллельно (обычно 6 на домен). Но это не масштабируется бесконечно — TCP-соединения тяжелы для сервера.
HTTP/2 решает это через мультиплексирование: одно TCP-соединение, но несколько одновременных «потоков» (streams) внутри него. Браузер шлёт 50 запросов параллельно — сервер отвечает в любом порядке.
Однако у HTTP/2 есть проблема: head-of-line blocking на уровне TCP. Если один TCP-пакет потерялся, TCP не отдаёт следующие пакеты приложению, пока не получит потерянный. Все streams в этом TCP-соединении ждут. Для HTTP/1.1 это не страшно (всё равно один запрос), а для мультиплексированного HTTP/2 — больно.
HTTP/3: QUIC поверх UDP
HTTP/3 и QUIC: мультиплексирование без HoL blockingHTTP/3 решает HoL blocking радикально — отказывается от TCP. Под капотом теперь QUIC (Quick UDP Internet Connections) — протокол поверх UDP, в котором мультиплексирование сделано на уровне транспорта. Если один пакет потерян — это влияет только на тот stream, к которому он относится. Остальные streams продолжают работать.
Дополнительные плюсы QUIC:
- 0-RTT handshake. Если вы уже подключались к серверу, повторное соединение занимает 0 round-trips (TCP+TLS традиционно — 2-3 RTT).
- Connection migration. Если вы переключились с Wi-Fi на 4G — QUIC-соединение продолжит работать. TCP бы разорвалось.
- Лучшая производительность на мобильных. Высокий jitter и потери на 4G/5G — QUIC справляется лучше.
Большинство современных серверов (Cloudflare, Google, Facebook) поддерживают HTTP/3, и браузеры автоматически переключаются. Для Junior DE это значит: пишете requests.get(url) — библиотека на стороне Python пока работает по HTTP/1.1 или HTTP/2 (requests не поддерживает HTTP/3 на май 2026). httpx тоже HTTP/2-only. HTTP/3 поддерживают aioquic и h3 — экспериментальные библиотеки.
Stateless: главная архитектурная фича HTTP
Вторая важнейшая характеристика HTTP — statelessness. Каждый запрос полностью независим от предыдущих. Сервер ничего не помнит между запросами — каждый запрос должен содержать всю информацию для обработки.
REST поверх HTTP — архитектурный стиль, а не протоколЗвучит странно: «но как же тогда работает авторизация? Каким образом я остаюсь залогинен на сайте?» Ответ: на каждом запросе клиент сам передаёт всё нужное. Cookie, Bearer-токен, session ID — это не «состояние сервера», это идентификатор, который клиент шлёт на каждом запросе.
Польза statelessness — фундаментальная:
- Horizontal scaling. Запрос можно отправить на любой из 100 серверов в кластере — все равноправны, ни один не «помнит» вас. Балансировщик нагрузки тупо распределяет запросы по round-robin.
- Простая отладка. Чтобы воспроизвести баг, достаточно того же запроса. Не нужно «восстанавливать сессию».
- Кэширование. Промежуточные прокси и CDN могут кэшировать ответы. Это работает, потому что запрос самодостаточен.
Минус: каждый запрос несёт overhead (заголовки, токен, cookies). Это решается компрессией заголовков (HTTP/2 HPACK, HTTP/3 QPACK) и persistent connections.
‘Сессия’ и ‘session ID в cookie’ — это иллюзия statefulness над stateless протоколом. Сервер не хранит вашу сессию в TCP-соединении. Он хранит её в Redis/БД, а cookie — это ключ. Каждый запрос вы шлёте этот ключ заново.
Реальный пример: что происходит при curl https://api.github.com/users/torvalds
Соберём всё вместе. Вот что физически происходит, когда вы выполняете эту команду:
Итого: ~150-500 мс на один запрос с нуля. Если вы шлёте 100 таких запросов — это 15-50 секунд тупого времени на handshake’ах. Поэтому в production HTTP-клиенты переиспользуют TCP-соединения (connection pooling, keep-alive). Подробно — в модуле 6.
Попробуй сам
Если установлен curl, посмотрите все слои HTTP-запроса своими глазами:
# Посмотреть полный verbose вывод HTTPS-запроса:
curl -v https://httpbin.org/get 2>&1 | head -50
# Посмотреть TLS-детали отдельно:
curl --trace-time -v https://httpbin.org/get 2>&1 | grep -E '^(TLS|SSL|<|>)'
# Узнать, по какой версии HTTP идёт связь (httpbin поддерживает HTTP/1.1 и HTTP/2):
curl -I -w 'HTTP version: %{http_version}\n' https://httpbin.org/get
# Сравнить с GitHub (поддерживает HTTP/2):
curl -I -w 'HTTP version: %{http_version}\n' https://api.github.com
# Посмотреть, поддерживает ли сервер HTTP/3 (через alt-svc заголовок):
curl -sI https://www.google.com | grep -i alt-svc
# Что-то вроде: alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Если в выводе curl -v посмотреть строки, начинающиеся с > — это что вы отправили. Со < — что вернулось. Это и есть raw HTTP-обмен в текстовом виде. На следующем уроке мы разберём этот формат до каждой строчки.