Перейти к содержанию
11 минут чтения
Тема: Диагностика Telegram Bot API

Telegram-бот отправляет сообщения, но не получает: что проверить в webhook

Сообщения из приложения уходят в Telegram, но команды пользователей не доходят до бота. Разберём, как отличить сбой доставки webhook от ошибки маршрута, фильтра событий или обработки в приложении.

Короткий ответ

Отправка сообщения и получение обновления проходят в разных направлениях. Успешный sendMessage подтверждает исходящий запрос, но не проверяет входящий webhook. Начните с зарегистрированного адреса и состояния доставки, затем проследите одно новое событие до обработчика.

Не удаляйте webhook и накопленные обновления ради проверки. Сначала сохраните текущие настройки и выясните, кто принимает события: ваше приложение или промежуточный сервис.

1. Определите, куда должно прийти обновление

При прямом подключении маршруты выглядят так:

Два независимых направления
Приложение → Telegram Bot API → сообщение в чате
Telegram → HTTPS-адрес webhook → обработчик приложения

Если обновления принимает шлюз, появляется дополнительный участок:

Получение обновлений через промежуточный сервис
Telegram → сервис-получатель → HTTPS-адрес вашего приложения → обработчик

Запишите ожидаемый адрес, имя бота и среду: production или тестовый проект. Частый источник путаницы — один токен в двух приложениях. Настройка webhook из тестового проекта меняет адрес получения событий для этого бота, поэтому production может перестать их получать.

Уточните и способ получения событий. При настроенном webhook getUpdates использовать нельзя. Если приложение рассчитано на long polling, проверять нужно его работающий процесс и конфигурацию, а не несуществующий входящий маршрут.

Для диагностики выберите одно безвредное действие в личном чате с тестовым ботом — например, команду, которая только отвечает текстом. Отметьте время с часовым поясом. Не используйте команды оплаты, рассылки или изменения заказов.

2. Посмотрите состояние через getWebhookInfo

Метод getWebhookInfo читает текущие настройки и не меняет webhook. Команда ниже предназначена для Bash на Linux с curl. Токен вводится скрыто и передаётся curl через стандартный ввод конфигурации, а не в аргументе процесса.

Bash · только чтение состояния Telegram
set +x
read -rsp 'Telegram bot token: ' TELEGRAM_BOT_TOKEN
printf '\n'
printf 'url = "https://api.telegram.org/bot%s/getWebhookInfo"\n' "$TELEGRAM_BOT_TOKEN" |
  curl -q --config - --silent --show-error \
    --connect-timeout 5 --max-time 15 \
    --write-out '\nHTTP=%{http_code}\n'
unset TELEGRAM_BOT_TOKEN

В ответе может быть секретный URL webhook. Не публикуйте полный вывод. Не включайте трассировку shell или curl с токеном. Если сам запрос завершается таймаутом, сначала используйте руководство по соединению с api.telegram.org; отсутствие ответа не говорит о состоянии webhook.

Сначала проверьте ok. При ok: false анализируйте ошибку API, а не отсутствующие поля result. Успешный ответ может выглядеть так — это вымышленный пример:

JSON · пример ошибки на стороне обработчика
{
  "ok": true,
  "result": {
    "url": "https://app.example.test/telegram/webhook",
    "has_custom_certificate": false,
    "pending_update_count": 3,
    "last_error_message": "Wrong response from the webhook: 500 Internal Server Error"
  }
}
Как интерпретировать состояние webhook
ПолеЧто проверить
urlСовпадает ли получатель с ожидаемым? Пустое значение означает, что webhook не установлен. Чужой адрес может принадлежать прежнему сервису или тестовому приложению.
pending_update_countКоличество ожидающих доставки обновлений. Сравните два наблюдения после нового тестового события. Рост очереди — повод искать сбой доставки; ноль сам по себе не доказывает обработку приложением.
last_error_message и last_error_dateПоследняя ошибка и время её возникновения. Старая запись может оставаться после восстановления: сопоставляйте её с временем нового теста.
ip_addressКакой адрес сейчас использует Telegram. При недавнем переезде сопоставьте его с ожидаемым публичным сервером.
allowed_updatesЕсли поле присутствует, проверьте, включён ли нужный тип события. Сообщение, нажатие кнопки и изменение сообщения — разные типы обновлений.

Определения полей приведены в справочнике WebhookInfo. Если URL не тот, сначала найдите приложение, которое его устанавливает. Иначе ручное исправление может быть перезаписано при следующем запуске этого приложения.

3. Проверьте публичный адрес и HTTPS

Для прямой доставки из Telegram нужен доступный извне HTTPS-адрес. В руководстве Telegram по webhook указаны поддерживаемые порты: 443, 80, 88 и 8443. HTTPS требуется на каждом из них; обычный HTTP на порту 80 не подходит.

  • Проверьте DNS, публичную IPv4-запись и доступность нужного порта. Адрес localhost или частный адрес контейнера недоступен Telegram из интернета.
  • Сертификат должен соответствовать имени домена и иметь корректную цепочку. Проверьте срок действия и конфигурацию TLS, особенно после смены домена или сервера.
  • Убедитесь, что firewall, CDN или WAF пропускают запросы к маршруту. Страница проверки браузера и JavaScript challenge не подходят для серверного webhook.
  • Используйте конечный HTTPS-адрес. Не рассчитывайте на перенаправление с HTTP, с www или на другой путь.

С другого сервера можно проверить соединение с публичным доменом. Замените адрес ниже своим; здесь намеренно нет секретного пути webhook.

Bash · проверка домена, не доставка события
curl -q --silent --show-error --output /dev/null \
  --connect-timeout 5 --max-time 15 \
  --write-out 'HTTP=%{http_code} connect=%{time_connect}s tls=%{time_appconnect}s total=%{time_total}s\n' \
  https://app.example.test/

Полученный HTTP-ответ показывает, что проверяющий сервер смог пройти соединение и TLS. Даже 404 на корне может быть нормальным. Эта проверка не подтверждает доступность из сети Telegram и не проверяет POST-маршрут webhook. Не отключайте проверку сертификата через -k: так можно скрыть причину ошибки.

Следующий источник фактов — access/error log веб-сервера за время теста. Если записи нет, проверьте также журналы CDN и балансировщика, правильность виртуального хоста и настройки логирования. Отсутствие записи только в одном файле ещё не доказывает, что запрос не приходил.

4. Проследите запрос внутри приложения

Открытие страницы в браузере проверяет GET. Webhook приходит POST-запросом с JSON, поэтому успешная загрузка сайта не проверяет нужный маршрут. Найдите именно тестовый POST и его ответ.

Ответы обработчика и направления диагностики
НаблюдениеЧто проверить дальше
301 / 302Редирект на другой домен, HTTPS, завершающий слеш или страницу входа. Исправьте зарегистрированный адрес либо правило маршрутизации.
404 / 405Путь и разрешённый метод. Проверьте префикс API, конфигурацию reverse proxy и наличие маршрута в текущем релизе.
401 / 403 / 419Авторизацию, проверку секрета, WAF и CSRF middleware. Код указывает направление проверки, но сам по себе не определяет причину.
500 / 502 / 503 / 504Логи приложения и веб-сервера, доступность PHP-FPM или другого runtime, БД и очереди. Ищите исключение на том же запросе.
2xx, реакции бота нетНе вернула ли успех заглушка? Сохранилось ли событие, выполнился ли обработчик, работает ли worker, не отфильтрована ли команда?

Для внешнего webhook обычно не подходит требование пользовательской сессии и браузерного CSRF-токена. Настройте отдельный маршрут с проверкой источника. Не отключайте защиту форм всего сайта. При прямой доставке Telegram может передавать настроенный secret_token в заголовке X-Telegram-Bot-Api-Secret-Token. Если запрос пересылает промежуточный сервис, используйте его механизм проверки источника.

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

В журнале достаточно фиксировать идентификатор бота, update_id, время, этап обработки и идентификатор задания. Полное тело сообщения и секретные заголовки для обычной диагностики не нужны. Сопоставление этих записей покажет, где остановилось событие: на приёме, в очереди или в бизнес-логике.

Обработка должна учитывать повторы. Сохраняйте ключ события, например пару «бот + update_id», и предотвращайте повторное выполнение одной бизнес-операции, в том числе при параллельных запросах. Один факт прихода webhook не гарантирует, что он придёт только один раз.

5. Если доставка выглядит исправной, проверьте само событие

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

  1. Сверьте имя бота и токен окружения. Сообщение мог получить другой бот с похожим именем.
  2. Проверьте фильтр allowed_updates, права бота и режим приватности в группе. Наличие бота в чате не означает получение любого типа событий.
  3. Проверьте разбор JSON: обработчик только поля message не обработает нажатие кнопки в callback_query.
  4. Исключите фильтры самого приложения: разрешённые чаты, список команд, состояние диалога, выключенный функционал.

Не используйте drop_pending_updates=true как способ «починить очередь»: это удаление накопленных обновлений. Аналогично, переключение на polling меняет рабочую схему, а не просто проверяет её. Такие действия требуют понимания, какие события и обработчики они затронут.

Как проверить результат исправления

  1. Создайте новое тестовое событие и запишите время. Не ориентируйтесь только на старую ошибку в getWebhookInfo.
  2. Подтвердите получение нужным маршрутом, HTTP-ответ и сохранение события.
  3. Проследите выполнение задания и ожидаемое действие бота. Убедитесь, что оно произошло один раз.
  4. Повторите исходный проблемный сценарий: например, нажатие кнопки или сообщение в группе.
  5. Посмотрите, перестала ли расти очередь и появляются ли новые ошибки в обычном режиме работы.

Успешная доставка и успешная обработка — две отдельные проверки. Сохраните обе вместе с найденной причиной: это полезнее, чем запись «после перезапуска заработало».

Если вы используете BotGate

BotGate получает обновления через webhook; getUpdates не поддерживается. Для этого варианта нужно проверить оба участка доставки.

В BotGate в настройках бота вы указываете URL доставки своего приложения. Telegram при этом должен отправлять обновления на адрес BotGate. Поэтому URL из getWebhookInfo и ваш конечный URL доставки различаются — это ожидаемо.

Участок Telegram → BotGate

Откройте карточку бота в кабинете. Проверьте, что бот активен, webhook включён, URL доставки заполнен, а зарегистрированный в Telegram адрес принадлежит BotGate. Карточка показывает состояние регистрации и, если они есть, ожидающие обновления и последнюю ошибку Telegram. Сообщение о чужом webhook или конфликте — повод проверить другую интеграцию с тем же токеном.

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

Участок BotGate → ваше приложение

Ищите входящий POST от BotGate в журналах своего сервера. Для сопоставления есть заголовки X-BotGate-Bot-Id и X-BotGate-Event-Id. Проверяйте X-BotGate-Signature: это HMAC-SHA256 от исходного тела запроса с Webhook Secret вашего бота. Подпись нужно вычислять по полученным байтам до декодирования и повторной сборки JSON.

Заголовок Telegram X-Telegram-Bot-Api-Secret-Token не заменяет подпись BotGate. Если обработчик раньше принимал запросы напрямую, обновите проверку источника по документации webhook BotGate.

BotGate ожидает HTTP 2xx в течение 10 секунд. Для неудачной доставки предусмотрено до трёх попыток всего: первая без заданной задержки, затем повторы через 60 и 300 секунд после соответствующей неудачи. Фактическое время зависит и от очереди. Бесконечного повторения нет.

При обращении в поддержку передайте публичный ID бота, время теста с часовым поясом, наблюдаемый статус и ID события, если запрос дошёл. Не передавайте токен, Webhook Secret, подпись или содержимое переписки. Если запросов на вашем сервере нет, эти сведения помогут проверить приём и доставку со стороны BotGate.

Шлюз может изменить маршрут доставки, но не исправит отсутствующий обработчик, неверную подпись или неработающий worker вашего приложения. О различиях способов подключения рассказываем в сравнении прокси и API-шлюза.

Источники и границы примеров

Примеры адресов и ответов вымышлены. Команды читают состояние API или проверяют соединение; изменения настроек webhook и отправка тестовых событий выполняются отдельно в вашей среде.