← Блог

API не отвечает: таймаут, connection reset или блокировка

Коротко: запрос к API проходит четыре слоя: DNS узнаёт адрес сервера, TCP устанавливает соединение, TLS договаривается о шифровании, HTTP передаёт сам запрос и ответ. «API не отвечает» значит, что запрос умер на одном из них, и какой это слой, видно по тексту ошибки. Таймаут и reset означают разное. При таймауте ответа не было вовсе. Connection reset by peer значит, что соединение оборвала другая сторона: сервер, прокси перед ним или оборудование на пути.

Четыре слоя запроса. У каждого свои ошибки и своя проверка

Сначала прочитайте ошибку#

Текст ошибки обычно сразу называет слой. Одна и та же проблема в разных инструментах звучит по-разному:

Что пишет инструментСлойЧто это значит
Could not resolve host (curl, код 6), ENOTFOUND (Node.js), no such host (Go), Name or service not known (Python на Linux)DNSАдрес сервера не нашёлся
Connection refused (curl, код 7), ECONNREFUSEDTCPСервер доступен, но на этом порту никто не слушает или файрвол отказал явно
Connection timed out after … milliseconds (curl, код 28), ETIMEDOUT, i/o timeoutTCPПакеты уходят в пустоту: неверный адрес, файрвол молча их отбрасывает или маршрут не работает
Connection reset by peer (curl, код 35 во время TLS, 56 позже), ECONNRESETTCP или TLSКто-то отправил сигнал сброса: упал сервер, прокси или оборудование на пути
SSL certificate problem (curl, код 60)TLSНе прошла проверка сертификата
Operation timed out … with 0 bytes received (curl, код 28)HTTPСоединение установлено, но сервер не ответил за отведённое время
502, 503, 504HTTPОтветил прокси или балансировщик: сам сервис за ним не отвечает

Коды curl удобно смотреть в переменной $? сразу после команды: curl https://api.example.com; echo $?.

Строки с таймаутом из таблицы появляются, когда задан --connect-timeout или --max-time. Без них curl ждёт, пока соединение не оборвёт система, это минута-две, и пишет Failed to connect to … after 75003 ms: Could not connect to server. Код при этом бывает и 28, и 7, в зависимости от версии. Поэтому первую проверку лучше делать с таймаутами.

Что пишет клиент?

DNS
TCP
TLS
HTTP
Сервис
Что случилось
Кто-то прислал сброс соединения: сервер, балансировщик или оборудование на пути. HTTP-ответа нет.
Как падает
Сразу. Чаще всего после Client hello, тогда это рукопожатие TLS (код 35). Позже будет код 56.
Где искать
Перезапуск сервера, прокси, лимиты. Если из другой сети проходит, вмешательство на пути.
Проверьте
openssl s_client -connect api.example.com:443 -servername api.example.com
В Tetiva
request failed: Get "https://api.example.com/health": read tcp 192.0.2.15:52876->203.0.113.10:443: read: connection reset by peer
Выберите текст ошибки или вставьте свой: где оборвался запрос и что проверить

Что значит connection reset by peer#

Connection reset by peer значит, что другая сторона соединения прислала TCP-пакет RST и оборвала его. «Другой стороной» для программы выглядит любой, кто ответил от имени сервера: сам сервер, балансировщик или прокси перед ним, файрвол на пути. HTTP-ответа при этом нет вовсе.

Та же ошибка в разных языках и программах:

  • Python на Linux: ConnectionResetError: [Errno 104] Connection reset by peer, на macOS [Errno 54]. В requests она завёрнута в ConnectionError: ('Connection aborted.', ConnectionResetError(104, ...)).
  • Windows: [WinError 10054] Удаленный хост принудительно разорвал существующее подключение, в старых программах socket error 10054.
  • Node.js: Error: read ECONNRESET или socket hang up с кодом ECONNRESET.
  • Java: java.net.SocketException: Connection reset.
  • Go: read tcp …: read: connection reset by peer.
  • curl: Recv failure: Connection reset by peer, код 56. Если сброс пришёл во время рукопожатия TLS, код будет 35, а в подробном выводе OpenSSL SSL_connect: Connection reset by peer.
  • SSH: kex_exchange_identification: read: Connection reset by peer. Проверка та же, только порт 22: nc -vz host 22. Если порт открыт, а SSH всё равно сбрасывает, на сервере обычно срабатывает лимит MaxStartups: слишком много незавершённых подключений, часто от сканеров. Бан fail2ban выглядит иначе, как Connection refused.

Если сброс приходит сразу после Client hello, переходите к шагу 3, про TLS.

Если reset бывает не всегда

Если сброс случается время от времени, а повтор проходит, чаще всего клиент отправил запрос в соединение, которое сервер уже закрыл по простою. HTTP-клиенты держат соединения открытыми (keep-alive) и отправляют в них следующие запросы, а сервер закрывает простаивающие раньше: у Node.js-сервера keepAliveTimeout по умолчанию 5 секунд. Помогает время простоя в пуле клиента меньше серверного и один повтор для идемпотентных запросов (GET, PUT, DELETE). Ещё одна частая причина: сервер перезапускается при выкладке и рвёт открытые соединения.

Пройдите по слоям сверху вниз#

Для начала запустите curl -v с коротким таймаутом. По тому, на какой строке остановился вывод, видно, до какого слоя запрос дошёл:

curl -v --connect-timeout 5 --max-time 20 https://api.example.com/health
DNS
TCP
TLS
HTTP
Сервис
Упал через 5,0 сэто --connect-timeout 5
1 мс10 мс100 мс1 с10 с
$ curl -v --connect-timeout 5 --max-time 20 https://api.example.com/health
* Host api.example.com:443 was resolved.
* IPv4: 203.0.113.10
* Trying 203.0.113.10:443...
* Connection timed out after 5006 milliseconds
curl: (28) Connection timed out after 5006 milliseconds
$ echo $?
28
Дальше:nc -vz -w 5 api.example.com 443и тот же запрос из другой сети
Семь прогонов curl -v: докуда дошёл вывод, через сколько всё упало и какой код вернул curl

1. DNS: нашёлся ли адрес

Если вместо строки Host api.example.com:443 was resolved curl сразу пишет Could not resolve host, дело в DNS. Проверьте, что отвечают разные резолверы:

dig +short api.example.com
dig +short api.example.com @1.1.1.1
dig +short api.example.com @77.88.8.8

Если ответы разные или один из резолверов не знает адреса, проблема в DNS, а не в API. Чтобы проверить сервер в обход DNS, подставьте адрес вручную: curl --resolve api.example.com:443:203.0.113.10 https://api.example.com/health.

С середины августа 2026 года часть абонентов нескольких российских провайдеров сообщает, что зашифрованный DNS Google и Cloudflare (DoH и DoT) у них перестал работать, а на обычные запросы к 8.8.8.8 и 1.1.1.1 отвечает другой резолвер. Это замеры пользователей на Хабре, официального подтверждения нет. Но если в коде или на сервере жёстко прописан внешний резолвер, проверить его стоит в первую очередь.

2. TCP: connection refused или connection timed out

Если соединение установилось, сразу после строки Trying 203.0.113.10:443... начинается TLS: строки ALPN и Client hello. Если их нет:

  • Connection refused пришёл сразу: сервер на месте, но порт закрыт. Проверьте порт в адресе и что сервис запущен.
  • Пауза, а потом Connection timed out: пакеты не доходят. Это может быть неверный адрес, файрвол или проблема на маршруте.

Проверить сам порт без HTTP можно командой nc -vz api.example.com 443, в Windows Test-NetConnection api.example.com -Port 443. Если у сервера есть и IPv4, и IPv6, сравните curl -4 и curl -6: иногда сломан только один из путей.

3. TLS: ошибка сертификата или reset после Client hello

После Client hello сервер должен ответить строкой Server hello и прислать сертификат. Если сертификат не прошёл проверку, curl напишет SSL certificate problem, и это отдельная история: про сертификаты Минцифры и ошибку unable to get local issuer certificate есть отдельная статья.

Хуже, когда рукопожатие обрывается без объяснений: Recv failure: Connection reset by peer сразу после Client hello или тишина до таймаута. Подробности покажет openssl s_client -connect api.example.com:443 -servername api.example.com. Если та же команда из другой сети проходит, а из вашей нет, что-то на пути вмешивается в соединение по имени сайта. Ровно так в августовских замерах выглядела проблема с DoT: TCP-соединение устанавливается, а потом приходит reset.

4. HTTP: что ответил сервер

Если TLS прошёл, а ответа нет, сервер принял запрос и завис. curl закончит его по --max-time с сообщением 0 bytes received. Если ответ есть, смотрите на код:

  • 502 и 504: до прокси вы дошли, а сервис за ним не ответил ему.
  • 503: сервис перегружен или на обслуживании. Иногда в заголовке Retry-After написано, когда вернуться.
  • 429: вы упёрлись в лимит запросов.
  • 403 без понятного тела: часто это защита от ботов или фильтр по стране и IP, а не права доступа.

На любой из этих ответов curl завершается с кодом 0: обмен по HTTP состоялся. Чтобы скрипт заметил ошибку, добавьте --fail: тогда код будет 22, а curl напишет The requested URL returned error: 502.

Сбой или блокировка#

По одной ошибке причину не понять. Помогает сравнение:

  1. Тот же запрос из другой сети. Мобильный интернет, сервер в другой стране, онлайн-проверка доступности. Если оттуда работает, а от вас нет, проблема на пути, а не в API.
  2. Страница статуса сервиса. У крупных API она есть, и о сбоях там пишут раньше, чем в чатах.
  3. Другой клиент с той же машины. Если браузер открывает адрес, а скрипт нет, дело в настройках скрипта: прокси, сертификатах, DNS.

Если запрос не проходит ниоткуда, сбой на стороне сервиса. Если только из одной сети, а шаги выше показали reset или тишину на TLS, похоже на вмешательство на пути. Писать «заблокировали» по одному reset не стоит: сервер, который перезапускается, выглядит точно так же.

Что видно в API-клиенте#

Тайминги по фазам (DNS, подключение, TLS) Tetiva не показывает, только общее время ответа, поэтому слой ищут curl'ом. Сами ошибки в клиенте выглядят так:

  • Таймаут. Tetiva ждёт ответа до 30 секунд и потом пишет request timed out after 30s. Пока запрос идёт, тикает счётчик секунд, а кнопка Cancel его прерывает.
  • DNS. Ошибка выглядит как DNS resolution failed for "api.example.com".
  • Подключение. Ошибки подключения, кроме DNS и таймаута, показаны строкой connection refused с адресом сервера. Для DNS, подключения и таймаута клиент подсказывает, что проверить.
  • Reset и обрыв TLS. Приходят исходным текстом после request failed:, например …: read: connection reset by peer.
Ошибка запроса в Tetiva: текст ошибки и подсказки, что проверить

Неудачная попытка остаётся в истории вместе с текстом ошибки. Фильтр по статусу ошибка покажет, с какого момента запрос перестал проходить. Последнюю удачную и первую неудачную записи можно выделить с зажатым Cmd (Ctrl в Windows и Linux) и нажать «Сравнить», чтобы увидеть, что изменилось в запросе.

Что приложить к обращению в поддержку#

Чтобы поддержке было проще разобраться, приложите:

  • полный вывод curl -v (заголовок Authorization замените заглушкой);
  • время запроса с часовым поясом;
  • ваш внешний IP и провайдер;
  • что ответил dig и откуда запрос проходит, а откуда нет.

Если запрос падает в браузере, вместо вывода curl приложите HAR-файл: как его записать и что из него вычистить.

Шпаргалка#

  • DNS: dig +short host, сравнить резолверы, curl --resolve в обход.
  • TCP: refused значит закрыт порт, долгий таймаут значит не доходят пакеты. nc -vz host 443.
  • TLS: SSL certificate problem это сертификат, обрыв после ClientHello это вмешательство на пути. openssl s_client.
  • HTTP: 0 bytes received значит сервер завис, 502/504 значит не отвечает сервис за прокси, 429 значит лимит.
  • Блокировка или сбой: тот же запрос из другой сети.