К основному контенту
← Все статьи
ИТСЕТИ24 МИН

Что происходит, когда вы открываете сайт: путь запроса от DNS до TLS

Подробный разбор того, что происходит между нажатием Enter и появлением страницы: URL и HSTS, DNS, TCP, рукопожатие TLS 1.3, HTTP/1.1–HTTP/3 и рендеринг в браузере — по шагам, со схемами, таблицами и примерами команд.

ШП
Шандаков Павел
Эксперт по информационной безопасности · 5 июня 2026 г.
01 / иллюстрация: secenta

Когда вы открываете браузер, набираете адрес и нажимаете Enter, между этим моментом и появлением первой картинки на экране проходит, как правило, от ста до восьмисот миллисекунд. За эту долю секунды происходит каскад событий, в котором участвуют десятки протоколов, несколько физических машин на разных континентах, криптографические вычисления и сложная подсистема рендеринга внутри самого браузера. Большинство пользователей воспринимают это как мгновенное «открылся сайт». Инженер, который понимает, что именно происходит на каждом шаге, видит совсем другую картину: цепочку запросов и ответов, каждый из которых можно измерить, оптимизировать, сломать и защитить.

В этой статье мы пройдём весь путь запроса максимально подробно — на примере открытия https://example.com. Это не случайный выбор: домен example.com зарезервирован документом RFC 2606 (а позже RFC 6761) специально для примеров и документации, поэтому на нём безопасно демонстрировать любые команды, не задевая чужую инфраструктуру. Мы разберём, как браузер превращает строку URL в сетевое соединение, как работает DNS, что происходит во время TCP-рукопожатия, как TLS устанавливает зашифрованный канал, как устроены HTTP-запрос и ответ, что делает сервер за своим публичным адресом, и как браузер превращает полученный HTML в пиксели на экране. Сразу оговоримся: конкретные IP-адреса example.com со временем меняются (так, исторические 93.184.216.34, а затем 93.184.215.14 относились к инфраструктуре Edgecast/Verizon, а с 2024 года домен обслуживается напрямую под управлением IANA/ICANN на других адресах), поэтому все адреса в примерах приведены иллюстративно и могут не совпадать с тем, что вернёт ваш dig сегодня.

Чтобы говорить точно, договоримся о модели. Существуют две классические модели сетевого взаимодействия. Семиуровневая модель OSI (ISO/IEC 7498-1) — академический эталон: физический, канальный, сетевой, транспортный, сеансовый, представления и прикладной уровни. Практическая же модель, по которой реально работает интернет, — это четырёхуровневый стек TCP/IP (описан в RFC 1122): канальный (link), межсетевой (internet, здесь живёт IP), транспортный (TCP/UDP) и прикладной (application, здесь HTTP, DNS, TLS). OSI удобна для разговора об абстракциях, TCP/IP — для понимания того, что происходит в реальных пакетах. Дальше по тексту мы будем опираться в основном на TCP/IP, упоминая уровни OSI там, где это проясняет картину.

Этапы пути запроса ложатся на эти уровни так: разбор URL и формирование HTTP-сообщения — прикладной уровень; DNS — тоже прикладной протокол, но обслуживающий все остальные; TCP и TLS — транспорт и слой поверх него; IP — межсетевой уровень; а Ethernet, Wi-Fi, оптика — канальный и физический. Один «клик» прошивает весь стек сверху вниз на вашей машине, летит через сеть в виде последовательности кадров и пакетов, поднимается вверх по стеку на сервере, и так же возвращается обратно. Понимание того, на каком уровне живёт каждая проблема, — половина успеха при отладке: «сайт не открывается» может означать сбой DNS, обрыв TCP, ошибку валидации сертификата или зависший JavaScript, и это совершенно разные классы неисправностей.

Путь запроса: браузер → DNS → TCP → TLS → HTTP → сервер → рендеринг


Разбор URL: из чего состоит адрес и что браузер делает до сети

Прежде чем уйти в сеть, браузер должен понять, что вы ввели. URL (а точнее URI — Uniform Resource Identifier, формализованный в RFC 3986) имеет строгую грамматику. Полная форма выглядит так:

text1 строк
1
scheme://[userinfo@]host[:port][/path][?query][#fragment]

Разберём https://example.com:443/search?q=tls#results по компонентам:

  • scheme (схема)https. Определяет протокол и поведение по умолчанию. Для https порт по умолчанию 443, для http — 80, для ftp — 21. По RFC 3986 единственный формально обязательный компонент URI — это именно scheme; всё остальное опционально. Authority (а вместе с ней и host) может отсутствовать — например, в mailto:user@example.com, urn:isbn:0451450523 или file:///etc/hosts (с пустым host). Для HTTP(S)-URL host, разумеется, присутствует практически всегда — без него некуда подключаться.
  • userinfo — необязательная часть user:password@. В современных браузерах для HTTP практически запрещена и считается фишинговым вектором (например, https://example.com@evil.tld визуально выглядит как поход на example.com, а реально ведёт на evil.tld), поэтому Chrome и Firefox её подсвечивают или вырезают.
  • hostexample.com. Доменное имя или IP-адрес. Может быть IPv4 (например, 93.184.215.14), IPv6 в квадратных скобках (например, [2606:2800:21f:cb07:6820:80da:af6b:8b2c]) или зарегистрированное имя. Скобки вокруг IPv6 обязательны, чтобы двоеточия адреса не путались с разделителем порта.
  • port:443. Если опущен, берётся порт по умолчанию для схемы.
  • path/search. Иерархический путь к ресурсу.
  • query?q=tls. Набор параметров ключ=значение, разделённых &.
  • fragment#results. Важная деталь: фрагмент никогда не отправляется на сервер. Он обрабатывается только на стороне клиента — браузер использует его для прокрутки к якорю или маршрутизации в SPA.

Перед отправкой браузер выполняет нормализацию URL по правилам RFC 3986: приводит схему и host к нижнему регистру (доменные имена регистронезависимы), применяет процентное кодирование (percent-encoding) к небезопасным символам — пробел становится %20, кириллица кодируется в UTF-8 байты, а интернационализированные домены (IDN) преобразуются в Punycode (xn--). Также схлопываются сегменты . и .. в пути и убираются избыточные слэши. Нормализация — не косметика: для same-origin policy и кэширования https://Example.COM/a и https://example.com/a должны считаться одним и тем же origin, иначе сломались бы и безопасность, и кэш.

Отдельного внимания заслуживает превращение http в https. Часто пользователь набирает просто example.com или явно http://, но соединение всё равно идёт по HTTPS — и это происходит ещё до любого сетевого запроса. Работает механизм HSTS (HTTP Strict Transport Security, RFC 6797). Сервер однажды присылает заголовок:

http1 строк
1
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

После этого браузер запоминает: для этого домена в течение max-age секунд (здесь два года) любой http://-запрос нужно внутренне переписать в https:// ещё до выхода в сеть. Важно понимать механику точно: RFC 6797 не предписывает для этого апгрейда никакого HTTP-статус-кода — никакого реального сетевого ответа не происходит, переписывание URL целиком локальное. В DevTools Chromium это отображается как условный 307 Internal Redirect, но это артефакт диагностики конкретного браузера, а не часть протокола (Firefox показывает иначе). Так закрывается окно для атаки sslstrip, при которой злоумышленник перехватывает первый незашифрованный запрос и удерживает жертву на HTTP.

Но что с самым первым посещением, когда HSTS ещё не записан? Для этого существует HSTS preload list — список доменов, зашитый прямо в исходный код браузеров (Chromium, Firefox, Safari) и пополняемый через сервис hstspreload.org. Домены из этого списка считаются HTTPS-only с самого начала, ещё до первого контакта. Флаг preload в заголовке выше как раз сигнализирует о согласии владельца домена попасть в этот список. Таким образом, для значительной части веба апгрейд до HTTPS — это решение, принятое локально, мгновенно и без единого сетевого пакета.


DNS: как имя превращается в адрес

Сеть не знает, что такое example.com — она оперирует IP-адресами. Преобразование имени в адрес выполняет DNS (Domain Name System), фундамент которого описан в RFC 1034 (концепции) и RFC 1035 (реализация), с десятками последующих дополнений. Это распределённая иерархическая база данных, и понять её устройство критически важно: DNS — одновременно одна из самых нагруженных, самых хрупких и самых атакуемых подсистем интернета.

В разрешении имени участвуют три типа серверов:

  • Stub-резолвер — это не сервер, а тонкий клиент внутри вашей ОС (часть libc, systemd-resolved, dnsapi.dll в Windows). Он не умеет искать сам — он просто формирует запрос и отправляет его рекурсивному резолверу, адрес которого получен по DHCP или прописан вручную (например, 1.1.1.1 или 8.8.8.8).
  • Рекурсивный резолвер (рекурсор) — сервер провайдера или публичный (Cloudflare, Google, Quad9). Именно он берёт на себя всю тяжёлую работу: проходит иерархию и возвращает stub-резолверу готовый ответ. У него есть большой кэш.
  • Авторитативные серверы — хранят «истину» о конкретных зонах: корневые (root), серверы доменов верхнего уровня (TLD) и серверы конкретного домена.

Ключевой момент: stub-резолвер задаёт рекурсивный запрос («дай мне финальный ответ»), а рекурсор для его выполнения делает серию итеративных запросов («скажи, кого спросить дальше»). Для example.com это выглядит так:

  1. Рекурсор спрашивает один из 13 логических корневых серверов (a.root-servers.net … m.root-servers.net, физически — тысячи anycast-инстансов по всему миру): «где example.com?». Корень не знает, но отвечает: «спроси серверы зоны .com, вот их NS-записи и адреса». Адреса корневых серверов резолвер знает заранее из вшитого файла подсказок (root hints).
  2. Рекурсор идёт к TLD-серверу .com (управляется Verisign): «где example.com?». Тот отвечает: «вот авторитативные NS-серверы этого домена».
  3. Рекурсор идёт к авторитативному серверу example.com и получает финальную A-запись с IP-адресом.

Рекурсивное разрешение DNS: stub-резолвер, рекурсор, корневые, TLD и авторитативные серверы

DNS оперирует ресурсными записями (RR). Вот основные типы, которые встречаются на пути запроса:

ТипНазначениеПример значения (иллюстративный)
AИмя → IPv4-адрес93.184.215.14
AAAAИмя → IPv6-адрес2606:2800:21f:cb07:6820:80da:af6b:8b2c
CNAMEКаноническое имя (алиас)www → example.com.
NSКакие серверы авторитативны для зоныa.iana-servers.net.
MXПочтовый сервер домена (с приоритетом)10 mail.example.com.
TXTПроизвольный текст (SPF, DKIM, верификация)v=spf1 -all
SOAStart of Authority: параметры зоны и TTLсерийный номер, refresh, expire, minimum

Значения IP в таблице исторические и приведены лишь как иллюстрация формата — актуальный адрес example.com сегодня иной.

Чтобы система не падала под нагрузкой, каждая запись имеет TTL (time to live) — сколько секунд её можно держать в кэше. Рекурсор кэширует ответы и отдаёт их без повторного обхода иерархии, пока TTL не истёк. Существует и negative caching (RFC 2308): отрицательные ответы (NXDOMAIN — домена нет — и NODATA — имя есть, но записи нужного типа нет) тоже кэшируются, чтобы не долбить авторитативные серверы запросами о несуществующих именах. Время такого кэша по RFC 2308 §5 задаётся минимумом из поля MINIMUM в SOA-записи и TTL самой SOA-записи, возвращённой в секции authority (а не TTL запрашиваемой записи, которой при NXDOMAIN попросту нет).

По транспорту DNS традиционно использует UDP на порту 53 — это быстро, без установки соединения. Исторический лимит размера DNS-сообщения по UDP без EDNS составлял 512 байт (RFC 1035 §2.3.4). Если ответ больше, сервер выставляет флаг TC (truncated), и резолвер повторяет запрос по TCP/53. Чтобы не платить за это, придумали EDNS0 (RFC 6891) — расширение, позволяющее анонсировать поддержку UDP-пакетов большего размера (часто 1232–4096 байт) и передавать дополнительные флаги (включая DO — DNSSEC OK). Без EDNS0 современный DNS с подписями просто не помещался бы в 512 байт и постоянно скатывался бы в TCP.

Классический DNS отправляется открытым текстом, что создаёт две проблемы — приватность (провайдер и любой на пути видят, что вы запрашиваете) и целостность (ответ можно подделать). Решения:

  • Приватность транспорта. DoT (DNS over TLS, RFC 7858, порт 853), DoH (DNS over HTTPS, RFC 8484, порт 443 — неотличим от обычного веб-трафика) и DoQ (DNS over QUIC, RFC 9250). Они шифруют канал между stub/браузером и рекурсором.
  • Целостность данных. DNSSEC (RFC 4033–4035) добавляет к записям криптографические подписи (RRSIG, DNSKEY, DS). Подписывается не отдельная запись, а RRset — весь набор записей одного имени и типа. В зоне обычно два ключа: KSK (Key Signing Key — подписывает только набор DNSKEY) и ZSK (Zone Signing Key — подписывает остальные RRset зоны); хеш KSK публикуется у родителя как запись DS. Так выстраивается цепочка доверия от корня: корневая зона через DS подтверждает ключ .com, .com — ключ example.com. Если хоть одно звено не сходится, валидирующий резолвер вернёт SERVFAIL. Важно понимать: DNSSEC обеспечивает аутентичность и целостность, но не конфиденциальность — для приватности нужны DoH/DoT.

Без этих механизмов DNS уязвим к спуфингу и отравлению кэша (cache poisoning). Классическая атака Камински (2008) использовала предсказуемость идентификаторов транзакций: злоумышленник заваливал резолвер поддельными ответами с угаданным 16-битным Query ID до прихода настоящего, подменяя записи в кэше. Защита — рандомизация Query ID и порта источника (RFC 5452), что увеличивает пространство угадывания с 16 до ~32 бит, а радикально — DNSSEC. CISA и профильные CERT регулярно публикуют рекомендации по защите DNS-инфраструктуры именно из-за её роли как точки отказа.

Посмотреть на всё это можно командой dig:

text6 строк
1
2
3
4
5
6
$ dig example.com A +noall +answer
example.com. 3600 IN A 93.184.215.14
$ dig example.com +dnssec +noall +answer
example.com. 3600 IN A 93.184.215.14
example.com. 3600 IN RRSIG A 8 2 86400 20260701000000 20260610000000 12345 example.com. <base64-подпись>

В строке RRSIG поля читаются так: A — тип покрываемого RRset; 8 — алгоритм подписи (RSA/SHA-256); 2 — число labels в имени; 86400 — original TTL; далее время истечения (expiration) и начала действия (inception) подписи в формате YYYYMMDDHHMMSS; 12345 — key tag (идентификатор ключа DNSKEY); example.com. — имя подписавшей зоны (signer); и наконец сама подпись в base64.


TCP: надёжный транспорт под капотом

Получив IP-адрес, браузер открывает транспортное соединение. Для классического HTTPS это TCP (Transmission Control Protocol, RFC 9293, консолидировавший старый RFC 793). IP-уровень сам по себе ненадёжен: пакеты могут теряться, дублироваться, приходить не по порядку. TCP надстраивает поверх этого надёжный, упорядоченный, потоковый канал: он нумерует байты, подтверждает доставку, переспрашивает потерянное и собирает поток в исходном порядке.

Соединение начинается с трёхстороннего рукопожатия (three-way handshake). Его задача — синхронизировать начальные порядковые номера (ISN, Initial Sequence Number) обеих сторон и подтвердить, что канал двусторонний:

text3 строк
1
2
3
Клиент → Сервер: SYN, seq=x (хочу соединиться, мой ISN=x)
Сервер → Клиент: SYN-ACK, seq=y, ack=x+1 (согласен, мой ISN=y, жду x+1)
Клиент → Сервер: ACK, seq=x+1, ack=y+1 (подтверждаю, соединение открыто)

Флаг SYN (synchronize) запрашивает синхронизацию, ACK (acknowledgment) подтверждает. Поле seq — номер первого байта в сегменте, ack — номер следующего ожидаемого байта. ISN не нулевой, а случайный — это защита от подмены и от наложения старых сегментов на новое соединение (RFC 6528). После третьего пакета соединение в состоянии ESTABLISHED, и данные можно слать. Здесь же кроется неизбежная плата: рукопожатие стоит один полный RTT (round-trip time) — туда и обратно — ещё до того, как уйдёт первый байт HTTP.

Соединение идентифицируется четвёркой (4-tuple): IP источника, порт источника, IP назначения, порт назначения. Серверный порт фиксирован (80 для HTTP, 443 для HTTPS), клиентский выбирается из эфемерного диапазона. TCP-соединение проходит через конечный автомат состояний: LISTEN → SYN-SENT → SYN-RECEIVED → ESTABLISHED → FIN-WAIT → TIME-WAIT → CLOSED (это упрощённый путь; полный автомат RFC 9293 включает также CLOSE-WAIT, LAST-ACK и CLOSING на стороне, закрывающей соединение второй). Состояние TIME-WAIT удерживает закрытое соединение на время, концептуально равное 2×MSL (Maximum Segment Lifetime), чтобы запоздавшие сегменты не попали в новое соединение с той же четвёркой. На практике это значение зависит от ОС: в Linux суммарный TIME-WAIT обычно около 60 секунд, а не строго 240.

Важные параметры размера. MTU (Maximum Transmission Unit) — максимальный размер кадра на канальном уровне, для Ethernet это 1500 байт. MSS (Maximum Segment Size) — максимум полезных данных в одном TCP-сегменте, обычно MTU минус заголовки IP и TCP (около 1460 байт для IPv4). Стороны анонсируют MSS в опциях SYN. Если пакет превысит MTU где-то на пути, он либо фрагментируется, либо (при флаге DF, Don't Fragment) отбрасывается с ICMP-сообщением «Fragmentation needed» — отсюда классическая проблема «PMTUD black hole», когда файрвол режет эти ICMP, и соединение зависает на отправке крупных пакетов при работающем «пинге».

TCP управляет скоростью двумя независимыми механизмами. Управление потоком (flow control) защищает получателя: каждая сторона анонсирует receive window (rwnd) — сколько байт она готова принять, чтобы не переполнить свой буфер. Управление перегрузкой (congestion control, RFC 5681) защищает сеть: отправитель не знает её ёмкости, поэтому начинает осторожно. В фазе slow start congestion window (cwnd) растёт экспоненциально — начальное окно по RFC 6928 равно 10 сегментам (IW=10), и при каждом полученном ACK окно увеличивается, удваиваясь примерно за RTT. Так продолжается, пока cwnd не достигнет порога ssthresh или не случится потеря; тогда алгоритм переходит в фазу congestion avoidance с линейным ростом (примерно +1 MSS за RTT). Потери обнаруживаются либо по таймауту, либо быстрее — по трём дублирующим ACK (механизм fast retransmit/fast recovery, а с SACK, RFC 2018, получатель точечно сообщает, какие именно блоки пришли). Конкретный алгоритм определяет поведение под нагрузкой: CUBIC (по умолчанию в Linux) — loss-based, наращивает окно по кубической функции от времени с последней потери; BBR (Google) принципиально иной — он не ждёт потерь, а строит модель узкого места, оценивая bottleneck bandwidth и минимальный RTT, и держит темп около произведения «полоса × задержка» (BDP).

Чтобы сэкономить тот самый RTT на рукопожатии, придумали TCP Fast Open (TFO, RFC 7413): при повторном соединении клиент предъявляет криптографический cookie, выданный сервером ранее, и может отправить данные прямо в SYN-пакете, не дожидаясь завершения рукопожатия. На практике TFO внедрялся тяжело из-за «залипания» промежуточных устройств (middleboxes), которые не понимают данные в SYN, и сегодня его роль во многом перехватил QUIC, о котором речь пойдёт в разделе про HTTP/3.


TLS: как канал становится защищённым

TCP-канал установлен, но он открытый — всё передаётся открытым текстом. Поверх него поднимается TLS (Transport Layer Security), который обеспечивает три свойства:

  • Конфиденциальность — данные шифруются, перехватчик видит только зашифрованный поток.
  • Целостность — любое изменение байтов на лету обнаруживается (через AEAD-шифры со встроенной аутентификацией).
  • Аутентичность — клиент убеждается, что говорит именно с example.com, а не с подставным сервером.

(Эти три свойства не следует путать с классической «CIA-триадой» информационной безопасности — Confidentiality, Integrity, Availability; доступность TLS как раз не гарантирует.)

Актуальная версия — TLS 1.3 (RFC 8446, 2018). Её главное отличие от TLS 1.2 — радикальное упрощение и ускорение. TLS 1.2 требовал двух RTT на рукопожатие и допускал устаревшие, небезопасные наборы шифров (RSA key exchange без forward secrecy, CBC-режимы, RC4). TLS 1.3 выбросил всё легаси: оставлены только AEAD-шифры (AES-GCM, ChaCha20-Poly1305), обмен ключами — только эфемерный (EC)DHE, а само рукопожатие сократилось до одного RTT, причём бо́льшая его часть теперь зашифрована.

Разберём рукопожатие TLS 1.3 по шагам:

  1. ClientHello. Клиент шлёт поддерживаемые версии, список шифронаборов, случайное число (client_random) и — ключевое новшество — расширение key_share с уже сгенерированной эфемерной публичной частью для обмена ключами. То есть клиент не ждёт, пока сервер выберет группу, — он угадывает её заранее (чаще всего X25519). Здесь же в открытом виде передаётся SNI (Server Name Indication) — имя запрашиваемого хоста, без которого один IP не смог бы обслуживать множество HTTPS-сайтов с независимыми сертификатами.
  2. ServerHello. Сервер выбирает шифронабор, шлёт server_random и свою часть key_share. После этого сообщения обе стороны уже могут вычислить общий секрет и ключи сессии.
  3. Зашифрованная часть. Всё дальнейшее идёт уже под шифрованием: EncryptedExtensions, Certificate (цепочка сертификатов сервера), CertificateVerify (подпись над хешем рукопожатия, доказывающая владение приватным ключом) и Finished (MAC всего рукопожатия для защиты от подмены и downgrade). Клиент проверяет сертификат, отвечает своим Finished — и канал готов к передаче HTTP.

Рукопожатие TLS 1.3 за один круг: ClientHello, ServerHello, зашифрованные сертификат и Finished

Обмен ключами строится на ECDHE (Elliptic Curve Diffie-Hellman Ephemeral). Слово ephemeral (эфемерный) здесь важнее всего: для каждой сессии генерируется новая пара ключей, которая после неё уничтожается. Это даёт forward secrecy (совершенную прямую секретность) — даже если злоумышленник позже украдёт приватный ключ сервера, он не сможет расшифровать ранее перехваченные сессии, потому что сессионные ключи нигде не хранились и не выводятся из долгоживущего ключа сервера. Сам по себе Диффи-Хеллман уязвим к MITM, поэтому он подкрепляется аутентификацией через сертификат (CertificateVerify).

Доверие к серверу обеспечивает PKI (Public Key Infrastructure, X.509, RFC 5280). Сертификат example.com подписан промежуточным CA, тот — корневым CA, чей сертификат уже лежит в доверенном хранилище ОС или браузера. Браузер строит цепочку от сертификата сервера до известного ему корня. Что именно проверяется:

ПроверкаЧто подтверждает
Срок действия (notBefore/notAfter)Сертификат не просрочен и уже вступил в силу
Соответствие имени (SAN)Домен из URL есть в Subject Alternative Name
Цепочка до доверенного корняПодписи валидны вплоть до корня в trust store
Назначение ключа (Key Usage / EKU)Сертификат разрешён для серверной аутентификации
Статус отзыва (CRL / OCSP)Сертификат не отозван CA досрочно
Certificate Transparency (SCT)Сертификат опубликован в публичных CT-логах

Отдельно про отзыв. Сертификат может быть скомпрометирован до истечения срока, тогда CA его отзывает. Проверяется это через CRL (Certificate Revocation List — длинные списки отозванных, неудобны) или OCSP (Online Certificate Status Protocol — точечный запрос статуса). У OCSP есть проблема приватности и задержки, поэтому применяют OCSP stapling (RFC 6066, Certificate Status Request): сервер сам периодически получает у OCSP-responder'а CA подписанный «штамп» о валидности и прикладывает его к рукопожатию, избавляя браузер от отдельного запроса; срок годности штампа ограничен полем nextUpdate. Современный Chrome дополнительно требует, чтобы для публично доверенного сертификата присутствовали SCT (Signed Certificate Timestamp) — доказательства публикации в логах Certificate Transparency; без них соединение будет отклонено. То есть CT — это не только постфактум-аудит, но и часть обязательной валидации.

Два важных нюанса современного TLS. Во-первых, SNI передаётся открытым текстом — это давняя дыра в приватности: даже при зашифрованном канале наблюдатель видит, к какому домену вы подключаетесь. Решает это ECH (Encrypted Client Hello) — расширение, шифрующее ClientHello целиком с помощью публичного ключа, опубликованного в DNS. Во-вторых, 0-RTT: при возобновлении сессии TLS 1.3 позволяет отправить данные прикладного уровня в первом же пакете, экономя RTT. Но за это платят безопасностью — 0-RTT данные уязвимы к replay-атакам (их можно перехватить и переслать повторно), поэтому в 0-RTT допустимы только идемпотентные запросы. На серверной стороне применяют anti-replay меры: одноразовые session tickets и «окна свежести» (freshness window), отбраковывающие повторы вне допустимого временного интервала; RFC 8446 явно предупреждает о рисках 0-RTT.


HTTP: язык, на котором говорят браузер и сервер

Зашифрованный канал готов — теперь по нему течёт HTTP (Hypertext Transfer Protocol), семантика которого сегодня формализована в RFC 9110, а версии — в RFC 9112 (HTTP/1.1), RFC 9113 (HTTP/2) и RFC 9114 (HTTP/3). HTTP — запрос-ответный протокол без состояния (stateless): сервер по умолчанию не помнит предыдущие запросы, состояние навешивается сверху через cookies и токены. Сама семантика (методы, статусы, заголовки) в RFC 9110 определена независимо от версии; меняется лишь кодирование на проводе — текстовое в 1.1 и бинарное в 2/3.

Запрос состоит из строки запроса (метод, цель, версия), заголовков и опционального тела:

http7 строк
1
2
3
4
5
6
7
GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, br
Connection: keep-alive

Ответ — из строки статуса, заголовков и тела:

http8 строк
1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1256
Cache-Control: max-age=600
ETag: "3f80f-1b6-3e1cb03b"
Date: Wed, 10 Jun 2026 12:00:00 GMT
<!doctype html><html>...</html>

Методы определяют намерение и обладают разными гарантиями. Здесь критически важны два свойства из RFC 9110: безопасный метод (safe — не меняет состояние сервера) и идемпотентный (idempotent — повторение даёт тот же результат, что одно выполнение).

МетодНазначениеБезопасныйИдемпотентный
GETПолучить ресурсдада
HEADКак GET, но только заголовкидада
OPTIONSУзнать возможности (используется в CORS preflight)дада
POSTСоздать/отправить данныенетнет
PUTЗаменить ресурс целикомнетда
PATCHЧастично изменитьнетнет
DELETEУдалить ресурснетда

Идемпотентность — не теоретическая тонкость: на ней строится логика повторных попыток. Прокси и клиенты могут безопасно повторить GET или PUT при таймауте, но не POST — иначе можно случайно создать два заказа. Именно поэтому платёжные API часто вводят свой ключ идемпотентности (Idempotency-Key), возвращая для повтора тот же результат вместо второй транзакции.

Статус-коды делятся на пять классов:

КлассЗначениеПримеры
1xxИнформационные100 Continue, 101 Switching Protocols
2xxУспех200 OK, 201 Created, 204 No Content
3xxПеренаправление301 Moved Permanently, 304 Not Modified, 307 Temporary Redirect
4xxОшибка клиента400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests
5xxОшибка сервера500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

Стоит запомнить пару тонкостей: 301 против 308 и 302 против 307 различаются тем, сохраняется ли метод при редиректе (307/308 гарантируют, что POST не превратится в GET); 304 Not Modified отдаётся без тела в ответ на условный запрос; 401 требует аутентификации (с заголовком WWW-Authenticate), тогда как 403 означает «вы опознаны, но доступ запрещён»; 429 обычно сопровождается заголовком Retry-After.

Ключевые заголовки, влияющие на путь запроса:

  • Кэширование. Cache-Control (max-age, no-cache, private, immutable, stale-while-revalidate) управляет временем жизни и стратегией обновления. ETag — версия ресурса; при повторном запросе клиент шлёт If-None-Match, и сервер может ответить 304 Not Modified без тела, экономя трафик и время.
  • Cookies. Сервер ставит Set-Cookie, клиент возвращает Cookie. Атрибуты Secure, HttpOnly, SameSite критичны для защиты от кражи сессий и CSRF.
  • CORS. Access-Control-Allow-Origin и компания управляют доступом из чужого источника — без них браузер блокирует кросс-доменные запросы (same-origin policy). «Непростые» запросы предваряются preflight-запросом OPTIONS.
  • Security-заголовки. Content-Security-Policy (защита от XSS), Strict-Transport-Security (HSTS), X-Content-Type-Options: nosniff, Referrer-Policy — рекомендованы OWASP Secure Headers Project.

Эволюция версий — это история борьбы с задержкой. HTTP/1.1 ввёл keep-alive (переиспользование TCP-соединения для нескольких запросов), но страдал от head-of-line blocking: запросы в одном соединении обрабатываются строго по очереди, медленный ответ блокирует все следующие. Обходили это открытием 6+ параллельных соединений на домен — дорого по ресурсам и плохо для congestion control.

HTTP/2 (RFC 9113) сделал протокол бинарным и ввёл мультиплексирование: множество логических потоков (streams) внутри одного TCP-соединения, фреймы которых идут вперемешку. Заголовки сжимаются алгоритмом HPACK — это статическая таблица типовых заголовков (например, :method: GET), динамическая таблица, накапливаемая на время соединения, и Huffman-кодирование строковых значений; вместе они убирают избыточность повторяющихся заголовков. HTTP/2 также вводил приоритизацию потоков (дерево зависимостей и веса) и server push, но на практике приоритизация оказалась капризной, а server push признан неэффективным и фактически свёрнут — его нишу заняли resource hints (preload/preconnect). Главное: HTTP/2 убрал HoL-блокировку на уровне HTTP — но не на уровне TCP. Потеря одного пакета по-прежнему тормозит все потоки, ведь TCP гарантирует строгий порядок байтов для всего соединения.

HTTP/3 (RFC 9114) решает и это, переехав с TCP на QUIC (RFC 9000) — транспорт поверх UDP. В QUIC потоки независимы на транспортном уровне, поэтому потеря пакета в одном потоке не блокирует остальные. Вдобавок QUIC объединяет транспортное и криптографическое рукопожатие (TLS 1.3 встроен), что даёт установку соединения за 1-RTT, а при возобновлении — 0-RTT. Сжатие заголовков в HTTP/3 берёт на себя QPACK — адаптация HPACK, устойчивая к переупорядочиванию пакетов.

Эволюция HTTP: 1.1 с блокировкой очереди, 2 с мультиплексированием, 3 поверх QUIC/UDP


Серверная сторона: что происходит до генерации ответа

С точки зрения браузера сервер — это один IP и один сертификат, но за ним обычно стоит целая цепочка компонентов. Запрос, придя на публичный адрес, сначала попадает на балансировщик нагрузки (L4 или L7), который распределяет соединения между множеством машин и часто терминирует TLS — то есть именно он, а не приложение, расшифровывает трафик и снимает с бэкенда дорогую криптографию.

Дальше запрос идёт на обратный прокси (reverse proxy, например nginx или Envoy). Он маршрутизирует по пути и хосту, отдаёт статику, ограничивает частоту запросов (rate limiting), фильтрует подозрительные запросы (часто в связке с WAF) и проксирует динамику на бэкенд. Само приложение (на любом стеке — Node, Go, Python, JVM) выполняет бизнес-логику. Здесь развилка по способу формирования ответа: для классической страницы сервер рендерит HTML-шаблон на сервере, для API — сериализует данные в JSON. И тот, и другой путь обычно требует обращений к базе данных и кэшам в памяти (Redis/Memcached), а нередко и синхронных вызовов соседних микросервисов; именно эти обращения, особенно медленные SQL-запросы и сетевые round-trip'ы между сервисами, чаще всего и определяют задержку. Время от входа запроса до первого байта ответа — это TTFB (Time To First Byte), и в нём «сидит» основная серверная задержка.

Поверх всего этого работает CDN (Content Delivery Network) — географически распределённая сеть кэширующих узлов. Кэш в вебе многослоен, и полезно держать в голове всю лестницу: кэш браузера → edge-узел CDN → origin shield → кэш приложения → база данных. Запрос за популярным ресурсом в идеале не доходит дальше ближайшего edge-узла. Эффективность слоя описывают через cache hit ratio (доля ответов, отданных из кэша без похода к origin); промах (miss) дороже, так как тянется к источнику. Важно, что CDN согласует своё поведение с теми же HTTP-заголовками из предыдущего раздела: Cache-Control и ETag задают TTL и условную ревалидацию на edge так же, как и в браузере, а директива stale-while-revalidate позволяет отдать слегка устаревшую копию мгновенно и обновить кэш в фоне, не заставляя пользователя ждать. Для example.com многие подресурсы вполне могли прийти не с исходного сервера, а с CDN-узла в вашем городе, что и срезает RTT, разгружает origin и поглощает всплески трафика, включая DDoS.


Рендеринг: как HTML становится пикселями

Браузер получил HTML — но это лишь текст. Превращение его в изображение называется critical rendering path и состоит из нескольких этапов.

Сначала парсер HTML строит DOM (Document Object Model) — древовидное представление документа. Параллельно CSS (из <style>, внешних файлов и инлайна) парсится в CSSOM (CSS Object Model). DOM отвечает на вопрос «что есть на странице», CSSOM — «как это выглядит». Эти два дерева объединяются в render tree — дерево только видимых узлов с применёнными стилями (элементы с display: none в него не попадают, в отличие от visibility: hidden, который место занимает).

Дальше — layout (он же reflow): браузер вычисляет геометрию — точные координаты и размеры каждого элемента в пикселях с учётом окна просмотра. После — paint: заливка пикселей слоёв (цвета, текст, тени, картинки). И наконец composite: браузер собирает отрисованные слои в правильном порядке (с учётом z-index, прозрачности, трансформаций), нередко на GPU, и выводит итоговый кадр. Понимание этой цепочки имеет прямую практическую цену: изменение геометрии (ширина, позиция) запускает дорогой reflow всего поддерева, тогда как анимация через transform и opacity обрабатывается на этапе composite и не трогает layout/paint — именно поэтому такие анимации «дешёвые» и плавные.

Эти же этапы напрямую отражаются в Core Web Vitals — пользовательских метриках Google, по которым измеряют реальную производительность: LCP (Largest Contentful Paint) — момент отрисовки крупнейшего видимого элемента, упирается в скорость paint и доставки ресурсов; CLS (Cumulative Layout Shift) — суммарный незапланированный сдвиг макета, то есть качество layout/reflow; INP (Interaction to Next Paint, сменивший FID) — отзывчивость на ввод, упирающаяся в занятость основного потока. Перевод абстрактных «layout/paint» в эти измеримые числа и есть язык, на котором веб-разработчики обсуждают рендеринг.

Критически важная деталь — JavaScript блокирует парсер. Когда HTML-парсер встречает <script> без атрибутов, он останавливается: скрипт нужно скачать и выполнить до продолжения парсинга, потому что он может менять DOM через document.write. Поэтому есть атрибуты:

  • defer — скрипт качается параллельно, выполняется после построения DOM, в порядке следования. Идеален для кода, зависящего от DOM.
  • async — качается параллельно, выполняется сразу как загрузился (порядок не гарантирован). Для независимых скриптов (аналитика).

CSS тоже блокирует рендеринг (но не парсинг DOM): браузер не отрисует страницу, пока не построит CSSOM, иначе мелькнул бы нестилизованный контент (FOUC). Поэтому критический CSS стараются доставить как можно раньше, а лучше — заинлайнить.

Наконец, при парсинге браузер встречает ссылки на подресурсы — картинки, шрифты, дополнительные скрипты и стили. Для каждого из них запускается новый цикл: возможно, новый DNS-резолв (если другой домен), новое TCP/TLS-соединение (или переиспользование существующего), новый HTTP-запрос. То есть весь путь, который мы разобрали, для одной страницы повторяется десятки раз. Браузер оптимизирует это: использует preload scanner (заранее сканирует HTML на ресурсы), переиспользует соединения и применяет приоритизацию. Разработчик может помочь явными resource hints: <link rel="dns-prefetch"> заранее резолвит DNS чужого домена, rel="preconnect" прогревает заодно TCP и TLS к нему, а rel="preload" форсирует раннюю загрузку конкретного критичного ресурса. По сути это способ запустить ранние этапы нашего пути (DNS → TCP → TLS) для будущих запросов заранее — и именно здесь цикл «открытия сайта» замыкается сам на себя.


Где живёт задержка и где живут атаки

Теперь, имея полную карту пути, разложим бюджет задержки и поверхность атаки. Каждый этап стоит времени — обычно измеряемого в RTT, — и каждый имеет свои угрозы. Для соединения с нуля характерна такая последовательность: DNS (часто 1 RTT, если нет в кэше), TCP-рукопожатие (1 RTT), TLS 1.3-рукопожатие (1 RTT), затем TTFB (запрос + работа сервера). На дальних маршрутах, где RTT составляет 100–150 мс, эти круги суммируются: только до первого байта контента легко набегает 300–450 мс ещё до того, как пойдёт сам HTML — отсюда ценность кэширования, keep-alive, TLS-resumption, 0-RTT и географически близких CDN-узлов, сокращающих сам RTT.

ЭтапИсточник задержкиТипичная угрозаЗащита
URL/HSTSsslstrip, downgrade на httpHSTS + preload
DNS1 RTT (промах кэша)cache poisoning, спуфингDNSSEC, DoH/DoT, рандомизация порта
TCP1 RTT (handshake)SYN-flood, RST-инъекцияSYN cookies, фильтрация
TLS1 RTT (1.3)MITM, fake/expired cert, downgradeвалидация цепочки, OCSP, CT, TLS 1.3
HTTPTTFBинъекции, XSS, CSRF, утечка cookieCSP, SameSite, валидация ввода
Рендерингпарсинг + ресурсыXSS через DOM, malicious scriptCSP, Subresource Integrity

Несколько слов об угрозах предметно. DNS-спуфинг подменяет ответ, уводя пользователя на сервер злоумышленника, — отсюда необходимость DNSSEC и шифрованного транспорта. MITM/downgrade пытается заставить стороны использовать слабую версию протокола или подсунуть свой сертификат; TLS 1.3 убрал уязвимые шифры и защищает рукопожатие от понижения версии, а HSTS не даёт откатиться на HTTP. Проблемы сертификатов (просрочка, отзыв, mis-issuance недобросовестным или скомпрометированным CA) частично решает прозрачность сертификатов — Certificate Transparency (RFC 6962): публичные append-only логи, куда обязаны попадать все выданные публично доверенные сертификаты, что позволяет владельцу домена заметить чужой сертификат на своё имя. На уровне HTTP живёт классика веб-безопасности: XSS, CSRF, инъекции — систематизированы в OWASP Top 10, а конкретные техники атак каталогизированы в MITRE ATT&CK и базе слабостей CWE (с привязкой к публичным CVE). Для оценки и координации уязвимостей существует FIRST (хранитель системы оценки CVSS), а практические руководства по защите инфраструктуры публикуют CISA и NIST (в частности, серия SP 800). Эта привязка к стандартам — не формальность: она даёт общий язык, на котором инженеры, аудиторы и вендоры обсуждают одни и те же риски.


Практика: смотрим путь запроса своими глазами

Теория обретает смысл, когда вы видите её в инструментах. Разберём три.

dig example.com показывает работу DNS. Полезные ключи: +trace проводит итеративный обход иерархии от корня (вы увидите делегации root → TLD → authoritative своими глазами), +dnssec запрашивает подписи, +short даёт только результат.

text5 строк
1
2
3
4
5
$ dig +trace example.com
. NS a.root-servers.net. ; корень
com. NS a.gtld-servers.net. ; делегация на .com
example.com. NS a.iana-servers.net. ; делегация на домен
example.com. A 93.184.215.14 ; финальный ответ (адрес иллюстративный)

curl -v https://example.com показывает TCP, TLS и HTTP разом. Флаг -v (verbose) печатает каждый шаг:

text11 строк
1
2
3
4
5
6
7
8
9
10
11
* Connected to example.com (93.184.215.14) port 443
* TLS 1.3 handshake, cipher TLS_AES_256_GCM_SHA384
* Server certificate: subject CN=example.com
* SSL certificate verify ok.
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.x
>
< HTTP/2 200
< content-type: text/html; charset=UTF-8
< cache-control: max-age=600

Строки с * — диагностика curl (соединение, TLS, проверка сертификата), > — отправленный запрос, < — полученный ответ. Здесь видно версию TLS, шифронабор, результат валидации сертификата, версию HTTP и заголовки ответа. Обратите внимание: в выводе запрос идёт по HTTP/2 (h2), хотя в разделе про HTTP мы для наглядности показывали сообщение в текстовом HTTP/1.1. Противоречия нет: клиент и сервер при рукопожатии (через расширение ALPN в TLS) согласуют наивысшую общую версию — обычно h2, а с QUIC и h3, — поэтому реальный вывод чаще бинарный, а текстовый формат удобен лишь для чтения человеком.

Вкладка Network в DevTools даёт визуальную хронологию. Открыв её и перезагрузив страницу, вы увидите водопад (waterfall) запросов, а для каждого — разбивку по фазам в подсказке Timing: Queued/Stalled, DNS Lookup, Initial connection, SSL, Request sent, Waiting (TTFB), Content Download. Это буквально все этапы нашей статьи, измеренные в миллисекундах для конкретного запроса. Вкладка Security покажет цепочку сертификатов и версию протокола, а колонка Protocol — h2 или h3, то есть какую версию HTTP реально согласовали стороны.


Заключение

Открытие сайта — это не одно действие, а слаженный конвейер: разбор URL и решение об HTTPS, превращение имени в адрес через иерархию DNS, надёжное TCP-соединение, криптографическое рукопожатие TLS, обмен HTTP-сообщениями, работа серверной инфраструктуры и, наконец, рендеринг в браузере. Каждый этап измерим, каждый можно ускорить и каждый имеет собственную модель угроз. Инженер, который держит в голове эту полную карту, отлаживает проблемы и проектирует защиту совсем иначе, чем тот, для кого «сайт просто открылся».

Всё описанное здесь можно не только прочитать, но и потрогать руками: на интерактивных стендах вводных курсов «Основы сетей» и «Как устроен веб» вы сами запускаете dig и curl, разбираете TLS-рукопожатие по пакетам и наблюдаете путь запроса от URL до пикселя в реальном времени.

[ Ещё по теме ]

Похожие статьи

ИБ
26 мин
Гайд · 9 июня 2026 г.

Управление уязвимостями: от сканера до приоритизации — и при чём здесь пентест

Детальное руководство по управлению уязвимостями: масштаб проблемы и таксономия (CVE/CWE/CPE), многофакторная приоритизация по CVSS, EPSS, KEV и SSVC, методологии пентеста и его место в процессе — со схемами, таблицами и разобранными примерами.

ШП
Шандаков Павел

Обсуждение · 0 комментариев

?
Войдите, чтобы оставить комментарий