# Диагностика таймаутов прокси: по одному этапу за раз

Source: https://ipvolt.com/ru/guides/proxy-timeout-troubleshooting
Markdown: https://ipvolt.com/ru/guides/proxy-timeout-troubleshooting.md
Language: ru

[ipvolt — главная](https://ipvolt.com/ru.md) / [Руководства](https://ipvolt.com/ru/guides.md) / Диагностика таймаутов прокси: по одному этапу за раз

Диагностика
Проверено: 2026-09-11
9 мин чтения
Автор: ipvolt

Разделяем задержки DNS, TCP, CONNECT, TLS и ответа прокси с помощью таймингов curl, затем задаём дедлайны запросов и решаем, безопасен ли повтор.

## С чего начать

Таймаут говорит лишь о том, что истёк таймер. Найдите последний завершённый этап, прежде чем менять дедлайн, шлюз или политику повторов.

## Составьте карту пути, на котором истёк таймаут

Для HTTPS-назначения через HTTP-прокси обычный путь таков: разрешить имя прокси, подключиться к нему по TCP, получить успешный туннель CONNECT, завершить TLS с целевым сервером, отправить запрос, затем получить заголовки и тело. HTTPS-прокси добавляет собственное TLS-рукопожатие перед CONNECT. У двух TLS-соединений раздельные настройки доверия.

В такой схеме имя целевого хоста обычно разрешает прокси. Поэтому быстрое локальное измерение DNS ничего не говорит о разрешении целевого имени на стороне прокси. SOCKS5 может разрешать имя локально или удалённо в зависимости от настройки клиента; выясните этот выбор, прежде чем интерпретировать ошибку DNS.

Начните с одной утверждённой небольшой GET-конечной точки и одного шлюза. Задавайте аутентификацию и маршрутизацию явно. Навигация в браузере или задание приложения могут также ждать пул соединений, редиректы, скрипты или локальную обработку; их таймаут не обязательно является таймаутом прокси.

## Зафиксируйте одну попытку без трассировки

Этот пример для shell требует curl 8.3 или новее, потому что curl сам импортирует переменные аутентификации. Проверьте curl --version, включая TLS-бэкенд и список возможностей. Задайте в PROXY_URL HTTP(S)-шлюз провайдера без учётных данных; https://proxy.example.invalid:8443 — намеренно нерабочая иллюстрация. Подставьте PROXY_USERNAME и PROXY_PASSWORD приватно. В примере используется Basic-аутентификация прокси; меняйте аутентификацию только на документированный провайдером метод.

Сохраните как proxy-timing.sh и запустите sh proxy-timing.sh. CHECK_URL по умолчанию равен https://example.com/; можно подставить небольшую HTTPS-конечную точку, которую вы имеете право тестировать. Используйте URL без встроенных учётных данных и чувствительных параметров. Скрипт отбрасывает тело, печатает выбранные числовые результаты и сохраняет код выхода curl. Он не следует редиректам и не повторяет запрос. Не запускайте его с включённой трассировкой shell. --disable остаётся первым, чтобы игнорировать настройки curlrc, а --noproxy с пустым значением не даёт унаследованному правилу NO_PROXY обойти выбранный шлюз.

### proxy-timing.sh · curl 8.3+ · один HTTPS GET

```sh
: "${PROXY_URL:?Set a credential-free HTTP(S) proxy URL}"
CHECK_URL=${CHECK_URL:-https://example.com/}
case "$PROXY_URL" in
  http://*|https://*) ;;
  *) printf '%s\n' 'PROXY_URL must use http:// or https://' >&2; exit 2 ;;
esac
case "$CHECK_URL" in
  https://*) ;;
  *) printf '%s\n' 'CHECK_URL must use https://' >&2; exit 2 ;;
esac
case "$PROXY_URL $CHECK_URL" in
  *'@'*|*'?'*|*'#'*)
    printf '%s\n' 'Use URLs without credentials, queries or fragments' >&2
    exit 2 ;;
esac

curl --disable --silent --fail --http1.1 \
  --noproxy '' --proxy "$PROXY_URL" --proxy-basic \
  --variable %PROXY_USERNAME --variable %PROXY_PASSWORD \
  --expand-proxy-user '{{PROXY_USERNAME}}:{{PROXY_PASSWORD}}' \
  --connect-timeout 5 --max-time 15 --retry 0 \
  --output /dev/null \
  --write-out 'exit=%{exitcode} http=%{http_code} tunnel=%{http_connect} dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} ready=%{time_pretransfer} first=%{time_starttransfer} total=%{time_total} bytes=%{size_download}\n' \
  "$CHECK_URL"
```

## Читайте вехи, а не независимые таймеры

Поля времени — это секунды, отсчитанные от начала передачи. Это не отдельные длительности, которые нужно складывать. Пример запускает новый процесс curl и запрашивает HTTP/1.1, чтобы один запрос было проще анализировать. Переиспользуемые соединения, мультиплексирование, цепочки прокси и пройденные редиректы требуют дополнительной интерпретации. Нулевое поле может отражать округление, недостигнутый этап или этап, который не применялся; оно не доказывает мгновенность операции.

Рассматривайте разности как подсказки только тогда, когда обе вехи в этом простом запросе завершились. Например, tls минус tcp для HTTP-прокси включает обмен CONNECT и рукопожатие с целевым сервером; это не измерение одного лишь TLS до целевого сервера. first минус ready включает отправку запроса и ожидание по сети и на сервере. Оно не выделяет время CPU целевого сервера.

- dns = time_namelookup: завершение разрешения имени на стороне клиента. При такой конфигурации прокси значимое имя — это шлюз, а не разрешение целевого имени на стороне прокси.
- tcp = time_connect: соединение с прокси установлено. Это не подтверждает, что туннель или соединение с целевым сервером работают.
- tls = time_appconnect: установка TLS завершена. ready = time_pretransfer: настройка протокола дошла до точки, где может начаться передача. HTTPS-прокси добавляет ещё одно рукопожатие; эти поля не дают отдельных таймингов для каждого хопа.
- first = time_starttransfer: первый байт ответа, включая предшествующую настройку. Интерпретируйте его вместе с tunnel и http; полученный ответ прокси не является свидетельством успеха на целевом сервере.
- total = time_total и bytes = size_download: общее время передачи и число скачанных байтов тела. HTTP 200 при exit 28 всё равно может означать незавершённую загрузку.

## Идите от последнего завершённого этапа

Используйте следующий порядок для примера с одним запросом. Остановитесь на первом неразрешённом этапе. Предлагаемые проверки сужают поиск; одни лишь тайминги не могут определить, какая машина потеряла пакет, или объяснить внутреннюю очередь провайдера.

- Нет адреса прокси: exit 5 означает, что curl не смог разрешить имя прокси. Проверьте написание шлюза и резолвер, доступный внутри фактического контейнера или сервиса. Exit 6 касается имени хоста, которое curl пытался разрешить локально; пересмотрите маршрутизацию и режим DNS, прежде чем винить разрешение целевого имени на стороне прокси.
- Нет TCP-соединения: exit 7 указывает на сбой соединения; exit 28 до завершения tcp может означать, что истёк бюджет на соединение. Проверьте настроенный порт, исходящий маршрут и файрвол из того же runtime. Мгновенный отказ и молчаливый обрыв сети требуют разных проверок.
- TCP установлен, tunnel по-прежнему 000: пригодный ответ CONNECT не зафиксирован. Для HTTPS-прокси незавершённым этапом может быть его собственная установка TLS. Для HTTP-прокси исследуйте согласование CONNECT, доступность шлюза и разрешённый целевой порт. Чтобы разделить сбои разрешения целевого имени, соединения и политики на стороне провайдера, нужна его диагностика.
- tunnel равен 407: следуйте руководству по аутентификации прокси. Увеличение таймаута не исправит учётные данные. Другие ответы CONNECT, отличные от 2xx, требуют проверки политики прокси и документированного провайдером значения ошибки; это не HTTP-статусы целевого сервера.
- tunnel равен 200, TLS с целевым сервером не завершён: прокси принял туннель, но установка HTTPS не завершилась. Exit 35 указывает на сбой TLS-рукопожатия, exit 60 — на сбой проверки сертификата. Проверьте имя целевого сервера, утверждённое доверие CA и часы; не отключайте проверку сертификатов. Таймаут здесь может возникать и из-за зависшего пути через туннель.
- TLS и ready завершены, http равен 000: исследуйте ожидание ответа целевого сервера, включая удалённое приложение и обратный путь через прокси. Проверьте утверждённую контрольную конечную точку через тот же шлюз, затем снова исходную; меняйте по одной переменной за раз.
- http равен 200, bytes остановился и exit равен 28: заголовки пришли, но тело не было получено в пределах лимита. Сравните ожидаемый размер ответа и прогресс передачи. Увеличение таймаута соединения на этот этап не влияет.
- http равен 504: HTTP-шлюз сообщил о собственном таймауте upstream. С --fail пример обычно завершается с exit 22. Это отличается от exit 28 в curl, когда истёк клиентский лимит времени; выясните, какой шлюз сформировал ответ, прежде чем менять дедлайн клиента.

## Выделите каждой попытке долю общего дедлайна задания

В curl --connect-timeout охватывает установку соединения, включая DNS и необходимые согласования протокола; это не только таймер TCP. --max-time охватывает всю передачу, включая установку соединения и передачу тела. Бюджет соединения находится внутри бюджета передачи. Пять и пятнадцать секунд в примере — стартовые значения для небольшой диагностики, а не гарантии сервиса и не универсальные значения по умолчанию для продакшена.

Приложению всё равно нужен общий дедлайн, охватывающий ожидание пула, попытки, backoff, обработку тела и очистку. Идите от лимита вызывающей стороны в обратную сторону. Для иллюстративного задания в двадцать секунд можно зарезервировать две секунды на локальную работу, дать восемь секунд на первую попытку, подождать одну секунду и разрешить не более восьми секунд на повтор, если остаётся достаточно времени. Используйте монотонные часы и ограничивайте каждую следующую попытку остатком бюджета; не сбрасывайте дедлайн задания при повторе.

Таймаут чтения сокета обычно ограничивает интервал ожидания данных, а не всё задание. Медленный прогресс может раз за разом его обходить. --speed-limit вместе с --speed-time в curl может прерывать устойчиво медленные передачи, но это политика минимальной пропускной способности, а не точная замена таймаута простоя чтения в приложении. Long polling и стриминг требуют политики, соответствующей их ожидаемым паузам, а также отмены и общего времени жизни там, где это уместно.

## Повторяйте только тогда, когда повторение безопасно и полезно

Базовый вариант намеренно не содержит повторов. После локализации сбоя ограниченный повтор может быть разумен для утверждённого GET или HEAD, чья прикладная семантика безопасна и идемпотентна, и для сбоя, который вы считаете временным. Семантика HTTP-методов — лишь отправная точка; конечная точка, которая выполняет действие несмотря на использование GET, требует отдельного анализа. Таймаут после записи оставляет результат неопределённым. Проверьте состояние операции или документированный механизм идемпотентности, прежде чем отправлять её повторно.

Для проверенного безопасного GET добавление --retry 1 --retry-max-time 20 разрешает не более одного повтора для поддерживаемых curl временных условий. Это не делает двадцать секунд строгим дедлайном задания: --max-time перезапускается для каждой попытки, а попытка, начатая внутри окна повторов, может завершиться после него. Сохраняйте внешний дедлайн, когда вызывающей стороне нужен жёсткий лимит. Учитывайте Retry-After и останавливайтесь, когда требуемое ожидание не умещается в бюджет; циклы повторов в приложении должны использовать ограниченный backoff с джиттером и лимит параллелизма.

- Не повторяйте раз за разом неизменные ошибки аутентификации, сертификата, URL или политики. Сначала исправьте конфигурацию.
- Не добавляйте --retry-all-errors в общий клиент как средство от таймаутов. Это распространяет повторы на сбои, восстановление после которых и побочные эффекты не были проанализированы.
- Не воспроизводите платёж, отправку формы, отправку сообщения или другой изменяющий состояние запрос лишь потому, что его ответ был потерян. Потерянный ответ не доказывает, что операция не выполнилась.
- Не используйте повторы, чтобы пробиться через блокировку или увеличить нагрузку на перегруженный целевой сервер. Снижайте параллелизм и соблюдайте ограничения сервиса.

## Что установили локальные проверки

Проверено 11 сентября 2026 года на curl 8.7.1 под macOS со сборкой SecureTransport/LibreSSL, указанной в его выводе. Контролируемые loopback-стенды отработали обмен CONNECT с HTTP-прокси и локальное HTTPS-назначение с явно доверенным тестовым сертификатом. Зависший CONNECT и зависшее TLS-рукопожатие с целевым сервером оба завершились с exit 28 около бюджета соединения в 0,3 секунды, но только во втором случае был tunnel=200. Отдельное зависшее TLS-рукопожатие с HTTPS-прокси также завершилось до CONNECT.

После завершения TLS зависшие заголовки и зависшее тело вместо этого достигали общего бюджета в 1,2 секунды. В случае с телом сохранились http=200 и один скачанный байт. Подставленный HTTP 504 вернул exit 22. Задержанный CONNECT увеличил разность tls минус tcp, подтвердив, что она включает установку туннеля. Стенд с повторами, окном повторов в две секунды и попытками по 0,7 секунды работал около 2,4 секунды, подтвердив, что окно повторов само по себе не является строгим общим дедлайном. Его итоговое поле total описывало последнюю попытку, поэтому заданию с повторами также нужно внешнее измерение прошедшего времени.

Эти стенды устанавливают поведение клиента при контролируемых сбоях. Они не измеряют производительность провайдера, не воспроизводят реальные сбои DNS, не проверяют каждую сборку curl и не показывают, как ведут себя HTTP/2, SOCKS или многозвенный прокси. Храните версию runtime, этап, статусы, тайминги, число байтов и число попыток вместе с инцидентом. Делитесь только очищенными деталями; URL целевых серверов, заголовки и трассировки могут содержать приватные данные. Примеры не устанавливают доступность сервиса ipvolt или поддержку шлюза.

## Перед выпуском

- Фиксируйте фактический runtime, схему прокси, схему назначения и режим DNS.
- Зафиксируйте одну явную попытку, прежде чем добавлять редиректы, параллелизм или повторы.
- Используйте коды статуса и завершённые вехи таймингов вместе, чтобы выбрать следующую проверку.
- Держите бюджеты соединения и передачи внутри общего дедлайна задания.
- Повторяйте только утверждённую безопасную идемпотентную работу в пределах оставшегося бюджета.

## Источники и дополнительное чтение

- [Everything curl: proxy connections and DNS responsibility](https://everything.curl.dev/transfers/conn/proxies.html)
- [curl manual: variables, numeric output and retry-window limits](https://curl.se/docs/manpage.html)
- [libcurl: connection-completion timing](https://curl.se/libcurl/c/CURLINFO_CONNECT_TIME.html)
- [libcurl: TLS-completion timing](https://curl.se/libcurl/c/CURLINFO_APPCONNECT_TIME.html)
- [libcurl: pre-transfer timing](https://curl.se/libcurl/c/CURLINFO_PRETRANSFER_TIME.html)
- [libcurl: time to first response byte](https://curl.se/libcurl/c/CURLINFO_STARTTRANSFER_TIME.html)
- [Everything curl: exit-code meanings](https://everything.curl.dev/cmdline/exitcode.html)
- [libcurl: connection timeout within the total timeout](https://curl.se/libcurl/c/CURLOPT_CONNECTTIMEOUT.html)
- [libcurl: average-speed timeout policy](https://curl.se/libcurl/c/CURLOPT_LOW_SPEED_LIMIT.html)
- [Requests: read timeouts are not whole-download limits](https://requests.readthedocs.io/en/latest/user/quickstart/#timeouts)
- [Everything curl: retry behavior](https://everything.curl.dev/usingcurl/downloads/retry.html)
- [RFC 9110: idempotent retry semantics and gateway statuses](https://www.rfc-editor.org/rfc/rfc9110.html)

## Связанные руководства

- [Прокси в curl: флаг -x, переменные окружения, SOCKS5, аутентификация](https://ipvolt.com/ru/guides/curl-proxy-setup.md)
- [Как исправить ошибку прокси 407, не гадая](https://ipvolt.com/ru/guides/fix-proxy-error-407.md)
- [Переменные окружения прокси: HTTP_PROXY и NO_PROXY](https://ipvolt.com/ru/guides/proxy-environment-variables.md)

## О ipvolt

Примеры используют обобщённые настройки прокси со ссылками на оригинальную техническую документацию. Поведение конкретного продукта уточняйте у своего провайдера. ipvolt пока в разработке.

[Читать оригинал на английском](https://ipvolt.com/guides/proxy-timeout-troubleshooting.md)

## Узнайте, когда откроется доступ.

ipvolt · В разработке

Мы строим прокси-инфраструктуру для разработчиков и команд, работающих с данными. Оставьте email, чтобы получить уведомление, когда ipvolt будет готов.

Одно письмо, когда откроется доступ. Больше ничего.

[Получить ранний доступ](https://ipvolt.com/ru/guides/proxy-timeout-troubleshooting#waitlist-closing)

[Конфиденциальность](https://ipvolt.com/privacy)

