cURL error 28: Operation timed out after 30001 milliseconds with 0 bytes received
#Что означает код 28
CURLE_OPERATION_TIMEDOUT (28) возникает, когда cURL не успел завершить операцию за заданное время. Различайте два случая: не удалось установить соединение (Connection timed out after N milliseconds) — проблема сети, файрвола, DNS или недоступного хоста; соединение установлено, но ответ не пришёл (with 0 bytes received) — медленный или зависший сервер, тяжёлый запрос, блокировка на стороне получателя. Подход к исправлению зависит от этого.
#Причины
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Сервер не отвечаетАвария, перегрузка. | Не открывается и из браузера. | Подождите, свяжитесь с владельцем. |
| Файрвол блокируетИсходящие порты. | nc не соединяется. | Откройте порт. |
| Проблемы DNS или IPv6Нет маршрута IPv6. | Долгая фаза namelookup. | Принудительно IPv4. |
| Слишком малый таймаут1–5 секунд для тяжёлого API. | Обрывы на длинных запросах. | Увеличьте CURLOPT_TIMEOUT. |
| Ограничение частоты запросовRate limit. | Ответы замедляются. | Снизьте частоту. |
| Хостинг режет исходящиеОбщий хостинг. | Часть адресов недоступна. | Запросите доступ у поддержки. |
#Порядок
- Проверьте доступность адреса командой curl прямо с сервера, где работаетЭто проверка для разработчика или администратора сервера, где выполняется PHP. Подключитесь по SSH к этому серверу, откройте терминал и выполните
curl -I --max-time 15 https://api.example.com/, заменив URL фактическим адресом;-Iиспользует HEAD и подходит не для каждого API, поэтому при ответе 405 проверьте разрешённый GET на тестовом адресе. Если даже обычный curl не подключается, ошибка не в настройке PHP, а в сети или доступности сервиса. Не публикуйте ключи доступа в командной строке и отчёте. - Определите, на какой фазе тратится времяЧтобы понять место задержки, в терминале выполните
curl -o /dev/null -sS -w 'dns=%{time_namelookup} tcp=%{time_connect} tls=%{time_appconnect} first=%{time_starttransfer} total=%{time_total}\n' https://api.example.com/с реальным URL.dns— разрешение имени,tcp— начало соединения,tls— согласование шифрования,first— ожидание первых данных. Сравнивайте значения по нескольким запросам, а не по одному запуску; если сервер возвращает 429/503, увеличение таймаута не решит ограничения API. - Проверьте DNS и IPv6На сервере Linux выполните
getent ahosts api.example.com(имя заменить) и посмотрите доступные IPv4/IPv6. По отдельности проверьтеcurl -4 --max-time 10 -I URLиcurl -6 --max-time 10 -I URL; если IPv6 не настроен, его проверка закономерно завершится ошибкой. Не фиксируйтеCURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4во всём приложении без доказательства проблемы IPv6: это лишит его IPv6-only адресов. - Проверьте исходящие правила файрвола и ограничения хостингаЕсли соединение не устанавливается, на сервере проверьте
nc -zv хост 443(действительные адрес и порт); при отсутствииncиспользуйтеcurl -v. На управляемом хостинге узнайте у поддержки, разрешён ли исходящий порт. Администратор проверяет локальные правила и облачные списки доступа; не открывайте все исходящие направления и не отключайте межсетевой экран ради проверки. Успех TCP не подтверждает корректный TLS или успешный ответ API. - Увеличьте таймауты в коде, если сервер действительно медленныйЕсли DNS и соединение работают, а сервер отвечает медленно, откройте PHP-файл с
curl_init()и настройте отдельноCURLOPT_CONNECTTIMEOUT => 5(секунды подключения) иCURLOPT_TIMEOUT => 20(предел всей операции). Выбирайте числа по требованиям вашего API и допустимой задержке страницы. При ошибкеcurl_exec()записывайтеcurl_errno,curl_errorи время запроса в закрытый журнал без токенов; проверьте, какой этап превышает предел. - Добавьте повторы с задержкойПовторяйте запрос только для временных сбоев (например, cURL error 28 или ответ 429/503), с ограниченным числом попыток и возрастающей задержкой. Для операций создания платежа, заказа или другой записи повторы допустимы лишь при поддержке ключа идемпотентности — иначе одна операция может выполниться дважды. Не повторяйте ошибки сертификата или отказ авторизации: сначала устраните первопричину. Проверьте, что вся цепочка попыток укладывается в общий лимит времени.
- Если API ограничивает частоту запросов, соблюдайте лимитыЕсли API возвращает HTTP 429, откройте в выводе запроса вкладку заголовков или используйте
curl -iи найдитеRetry-After(если передан). Уменьшите параллелизм и частоту задач в очереди или планировщике, а задержку считайте по документации провайдера; не выбирайте произвольный быстрый цикл повторов. После правки повторите несколько запросов и убедитесь, что не появляются новые 429/таймауты. - Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
#Диагностика в расширенном режиме
Включите подробный вывод cURL: curl_setopt($ch, CURLOPT_VERBOSE, true) с записью в файл через CURLOPT_STDERR. В журнале видно, где остановился обмен: после Trying 203.0.113.5:443... — соединение не устанавливается; после SSL connection using — ждёт ответ приложения. Сравните результат с запросом из командной строки: если консольный curl быстрый, а PHP медленный, проверьте настройки прокси в php.ini и переменные окружения http_proxy.