# Анатомия медленного запроса: куда уходят миллисекунды

> Сайт «тормозит», но что именно: сеть, рукопожатия или сам бэкенд? HTTPS-запрос раскладывается на пять фаз, каждую можно измерить одной командой.

«Сайт медленный» звучит как диагноз, но это жалоба. Пока не видно, на какую фазу запроса ушло время, чинить можно что угодно: хостера, nginx, CDN или собственные нервы.

## Из чего состоит запрос

Первый HTTPS-запрос к серверу по новому соединению проходит пять фаз. Почти все они стоят целое число RTT (round-trip time, время «туда и обратно» до сервера), поэтому расстояние до сервера умножается на число рукопожатий.

**Схема:** Клиент → (1 RTT) TCP готов → (1 RTT) TLS 1.3 готов → (1 RTT + сервер) Первый байт → (загрузка) Ответ скачан

*Минимальная цена первого запроса по новому соединению с TLS 1.3 (по RFC 9293 и RFC 8446)*


| Фаза | Что происходит | Сколько стоит |
|---|---|---|
| DNS | Резолвер ищет A/AAAA. Если ответ есть в кэше, почти бесплатно | от ~0 до сотен мс, зависит от резолвера и TTL |
| TCP | SYN → SYN/ACK → ACK. Клиент считает соединение установленным, получив SYN/ACK (RFC 9293, рис. 6) | 1 RTT |
| TLS 1.3 | ClientHello → ServerHello…Finished → Finished, после чего клиент уже шлёт данные (RFC 8446, рис. 1) | 1 RTT; ещё +1, если сервер ответил HelloRetryRequest |
| TLS 1.2 | Полное рукопожатие: две пары сообщений до первых данных (RFC 5246, рис. 1). False Start сокращает его до одного RTT (RFC 7918) | 2 RTT |
| Ожидание ответа (TTFB) | Запрос долетает до сервера, сервер думает, первый байт летит обратно | 1 RTT + время бэкенда |
| Загрузка | Тело ответа | зависит от размера и канала |

В TLS 1.3 есть и 0-RTT: при повторном подключении клиент шлёт данные сразу. Но RFC 8446 прямо предупреждает, что у таких данных нет защиты от повтора (replay), поэтому включать 0-RTT для запросов, которые что-то меняют, нельзя.

В QUIC (HTTP/3) криптографическое и транспортное рукопожатие совмещены (RFC 9000, раздел 7), отдельной фазы TCP там нет.

## Реальный замер: где сеть, а где сервер

Globalping отдаёт тайминги по фазам, а не накопительно. Согласно спецификации API: `dns` время резолва, `tcp` от конца DNS до установленного TCP, `tls` от TCP до готового TLS, `firstByte` от готового соединения до первого байта ответа, `download` от первого байта до конца.

Вот запрос к `wikipedia.org` с домашней пробы в Москве (сеть Timeweb):

| Фаза | Начало, мс | Длительность, мс |
|---|---:|---:|
| DNS | 0 | 1 |
| TCP | 1 | 41 |
| TLS | 42 | 47 |
| Ожидание ответа | 89 | 42 |
| **Всего** | | **131** |

*GET https://wikipedia.org/ с домашней пробы Globalping, Москва (Timeweb), TLS 1.3, ответ 301; измерение 2WKLWjmKdGA5YlAJj00021Bt7, 24.09.2026*


Все три сетевые фазы примерно равны: 41, 47 и 42 мс. Это и есть RTT до сервера, помноженный на три рукопожатия. Серверу на ответ-редирект понадобилась миллисекунда-другая, загрузка заняла 0 мс. Такой запрос ускоряется только одним способом: сократить число RTT или сам RTT.

А вот `habr.com/ru/articles/` из той же пробы:

| Фаза | Начало, мс | Длительность, мс |
|---|---:|---:|
| DNS | 0 | 6 |
| TCP | 6 | 2 |
| TLS | 8 | 3 |
| Ожидание ответа | 11 | 225 |
| Загрузка | 236 | 1 |
| **Всего** | | **237** |

*GET https://habr.com/ru/articles/ с домашней пробы Globalping, Москва (Timeweb), TLS 1.3, ответ 200; измерение 2mP4kZK2aazEwqUJW00021Bt9, 24.09.2026. Загрузка не показательна: API Globalping возвращает не больше 10 КБ тела*


RTT около 2 мс, сервер буквально рядом, а первый байт пришёл через 225 мс. Около 220 мс из них бэкенд генерировал страницу. Сеть тут не виновата, и CDN перед таким сайтом не поможет, если страница не кэшируется.

> [!IMPORTANT]
> Главное правило чтения: **большой TTFB при быстрых DNS, TCP и TLS означает медленный сервер, а не медленную сеть.** Если все фазы примерно равны и велики, проблема в расстоянии. Если одна фаза выбивается, смотри на неё.

## Одна страница из разных стран

Та же проверка `wikipedia.org`, общее время по пробам:

| | мс |
|---|---:|
| Фалькенштайн, Hetzner | 42 |
| Амстердам, DELUXHOST | 73 |
| Сингапур, OVH | 80 |
| Хельсинки, Hetzner | 100 |
| Москва, Timeweb (дом.) | 131 |
| Москва, Yandex.Cloud | 148 |
| Санкт-Петербург, Selectel | 190 |
| Курск, Kursktelecom (дом.) | 325 |
| Новосибирск, МТС (дом.) | 334 |
| Томск (дом.) | 337 |
| Алматы, NLS Kazakhstan (дом.) | 1825 |

*Полное время GET https://wikipedia.org/ по пробам Globalping, измерение 2WKLWjmKdGA5YlAJj00021Bt7, 24.09.2026. «дом.» означает домашнего провайдера*


Что тут видно:

- **Сингапур быстрее Москвы**, хотя дальше от Европы. DNS Википедии отдал сингапурской пробе другой адрес (`103.102.166.224` вместо `185.15.59.224`), и TCP до него занял 1 мс. Это и есть «ближе к пользователю»: точка присутствия рядом снимает сразу все RTT-фазы.
- **Сибирь медленнее Москвы в 2,5 раза**: TCP 90 мс вместо 41. Каждое рукопожатие повторяет эту разницу.
- **Алматы**: фаза `tcp` 1122 мс при TLS 124 мс. Раз TLS (один RTT) занял ~120 мс, то TCP-рукопожатие за 1122 мс похоже на повтор SYN: по RFC 6298 начальный таймаут повторной передачи равен 1 секунде. В проверке `github.com` той же минутой у пробы из той же сети `tcp` был 1101 мс. Если так, один потерянный пакет на рукопожатии добавил к запросу целую секунду.
- **Курск**: DNS 113 мс, дольше, чем TCP. Холодный кэш резолвера бьёт по первому запросу.

## Замер у себя: curl -w

У curl тайминги **накопительные**, от начала запроса (man curl, раздел `--write-out`): `time_namelookup` до конца резолва, `time_connect` до установленного TCP, `time_appconnect` до готового TLS, `time_starttransfer` до первого байта, `time_total` всё целиком. Длительность фазы — разность соседних значений.

Готовый формат в файле:

```bash
cat > ~/curl-phases.txt <<'EOF'
     dns: %{time_namelookup}\n
 connect: %{time_connect}\n
     tls: %{time_appconnect}\n
    ttfb: %{time_starttransfer}\n
   total: %{time_total}\n
    http: %{http_version}  ip: %{remote_ip}  code: %{http_code}\n
EOF
curl -so /dev/null -w @$HOME/curl-phases.txt https://example.com/
```

Или сразу по фазам в миллисекундах:

```bash
curl -so /dev/null -w '%{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}\n' https://example.com/ \
  | awk '{printf "DNS %.0f  TCP %.0f  TLS %.0f  сервер %.0f  загрузка %.0f (мс)\n", $1*1000, ($2-$1)*1000, ($3-$2)*1000, ($4-$3)*1000, ($5-$4)*1000}'
```

Реальный вывод для `habr.com/ru/articles/` с машины автора, 24.09.2026:

```text
DNS 6  TCP 17  TLS 24  сервер 240  загрузка 169  (мс)
```

Картина та же, что у Globalping: сетевые фазы в десятки миллисекунд, сервер в сотни. Загрузка тут настоящая, curl скачал всю страницу (около 340 КБ).

> [!TIP]
> Меряй несколько раз. Тот же `github.com/torvalds/linux` с той же машины в один заход дал TTFB 496 мс, в следующий 73 мс. Одиночный замер TTFB говорит о кэше бэкенда не меньше, чем о бэкенде.

Похожую однострочную команду до `api.telegram.org` сообщество держит в наборе [тестов VPS](/baza/hosting/testy-vps/).

## Что с этим делать

| Что видно | Причина | Что делать |
|---|---|---|
| Все сетевые фазы равны и велики | Далеко до сервера | Сервер или CDN ближе к аудитории. По опыту сообщества, у части EU-хостеров нет прямых стыков с РФ, отсюда лишние десятки мс ([Прочее по хостингам](/baza/hosting/prochee/)) |
| TCP + TLS платятся на каждый запрос | Нет переиспользования соединений | keep-alive: HTTP/1.1 по умолчанию держит соединения постоянными (RFC 9112, раздел 9.3); HTTP/2 гонит много запросов по одному соединению (RFC 9113) |
| TLS занимает два RTT | TLS 1.2 | Включить TLS 1.3 на сервере |
| Потери на плохих каналах тормозят всё | HTTP/2 поверх TCP: потерянный пакет останавливает все потоки (RFC 9114, раздел 1.1) | HTTP/3 (QUIC), где потоки независимы |
| DNS долгий на первом запросе | Холодный кэш резолвера, маленький TTL | Не ставить TTL в секунды без нужды: TTL и есть время, сколько запись можно кэшировать (RFC 1035) |
| TTFB большой при быстрой сети | Бэкенд | Профилировать приложение, кэшировать ответы, смотреть базу. Сеть тут ни при чём |

Эффект keep-alive легко увидеть: если дать curl два URL одной командой, второй запрос пойдёт по тому же соединению (переменная `num_connects` покажет 0).

```bash
curl -s -o /dev/null -o /dev/null \
  -w 'conns=%{num_connects} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer}\n' \
  https://github.com/torvalds/linux https://github.com/torvalds/linux
```

В замере 24.09.2026 первый запрос: `conns=1`, TLS готов на 54 мс, первый байт на 73 мс. Второй: `conns=0`, connect и tls по нулям, первый байт на 19 мс.

## Проверить на host.pink

- [host.pink/check/example.com](https://host.pink/check/example.com): HTTPS-запрос с ~12 проб по миру, включая домашних провайдеров РФ. В каждой строке общее время, TLS и TTFB. В терминале: `curl "host.pink/check/example.com?t=http"`. Под результатом есть ссылка на тот же замер на globalping.io с полными таймингами.
- [Досье домена](/example.com): NS, DNS-записи, сертификаты. Полезно, когда тормозит именно DNS.
- Если сеть в порядке, а из РФ не открывается вообще, это уже другой разбор: [Сайт не открывается из РФ](/gaidy/sayt-ne-otkryvaetsya-iz-rf/). Про TTL и резолверы в сообществе: [DNS](/baza/set/dns/).

## Источники

- [RFC 9293](https://www.rfc-editor.org/rfc/rfc9293), раздел 3.5, рис. 6: трёхстороннее рукопожатие, клиент в ESTABLISHED после SYN/ACK
- [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446): рис. 1 (полное рукопожатие TLS 1.3), раздел 2.1 (HelloRetryRequest), раздел 2.3 и 8 (0-RTT и replay)
- [RFC 5246](https://www.rfc-editor.org/rfc/rfc5246), рис. 1: полное рукопожатие TLS 1.2; [RFC 7918](https://www.rfc-editor.org/rfc/rfc7918): False Start «сокращает задержку рукопожатия до одного RTT»
- [RFC 6298](https://www.rfc-editor.org/rfc/rfc6298), раздел 2: начальный RTO 1 секунда
- [RFC 9000](https://www.rfc-editor.org/rfc/rfc9000), раздел 7: совмещённое рукопожатие QUIC; [RFC 9113](https://www.rfc-editor.org/rfc/rfc9113), раздел 2: мультиплексирование HTTP/2; [RFC 9114](https://www.rfc-editor.org/rfc/rfc9114), раздел 1.1: блокировка всех потоков HTTP/2 при потере пакета
- [RFC 9112](https://www.rfc-editor.org/rfc/rfc9112), раздел 9.3: постоянные соединения HTTP/1.1; [RFC 1035](https://www.rfc-editor.org/rfc/rfc1035), раздел 3.2.1: TTL
- [curl(1), `--write-out`](https://curl.se/docs/manpage.html#-w): `time_namelookup`, `time_connect`, `time_appconnect`, `time_starttransfer`, `time_total`, `num_connects`
- [Спецификация API Globalping](https://api.globalping.io/v1/spec.yaml): значения `timings` (dns, tcp, tls, firstByte, download), тело ответа ограничено 10 КБ
- Замеры 24.09.2026: Globalping `2WKLWjmKdGA5YlAJj00021Bt7` (wikipedia.org), `2zj11OrwPfFIQdNdZ00021Bt8` (github.com), `2mP4kZK2aazEwqUJW00021Bt9` (habr.com); curl 8.18 с машины автора
- База: [Тесты VPS](/baza/hosting/testy-vps/), [Прочее по хостингам](/baza/hosting/prochee/), [DNS](/baza/set/dns/)

---
Источник: https://host.pink/gaidy/anatomiya-medlennogo-zaprosa/
Лицензия: CC BY-SA 4.0
