Telegram не доставляет обновления на бота Bothost. Ниже чтение getWebhookInfo: чем отличаются 404, SSL error, таймаут соединения и растущий pending_update_count, и что именно на платформе отвечает за каждый случай.

Для людей и ИИ. Каноническая страница: https://bothost.ru/docs/telegram-webhook-errors · Markdown: https://bothost.ru/docs/telegram-webhook-errors.md · Индекс: https://bothost.ru/llms.txt
Отвечает на запросы: Telegram webhook not working, webhook 404, webhook SSL error, Wrong response from the webhook: 404 Not Found, certificate verify failed, Connection timed out, pending_update_count, setWebhook, WEBHOOK_URL.
Как устроены домен, порт и 0.0.0.0: Веб-приложения и домены. Если в логе Conflict и бот на polling, откройте бот не отвечает.

Что Bothost делает с webhook, а что остаётся в коде

На бесплатном тарифе домена и webhook нет: бот получает апдейты через long polling. Webhook доступен на платном тарифе вместе с доменом. Когда домен включён, платформа:
  • выпускает HTTPS для *.bothost.tech или для подключённого своего домена;
  • передаёт в контейнер PORT и WEBHOOK_URL вида https://имя.bothost.tech/webhook;
  • направляет внешний HTTPS на процесс, который слушает 0.0.0.0 и этот PORT.
Платформа сама метод Telegram setWebhook не вызывает. Регистрация URL делается в вашем коде или вручную через браузер. Подчёркивание в идентификаторе бота в имени хоста заменяется на дефис: в setWebhook нужен тот хост, который показан в панели. Если в репозитории нет своего HTTP-сервера, а домен включён, при сборке добавляется служебная обёртка. Она отвечает на GET /health и запускает файл бота отдельным процессом. Маршрута POST /webhook у обёртки нет. Запрос Telegram на https://домен/webhook получает 404 от обёртки, а бот в это время сидит на polling и апдейт не видит. Чтобы принимать webhook, HTTP-обработчик этого пути должен быть в вашем приложении (aiohttp, aiogram webhook, FastAPI, Flask, Express). Обёртка тогда не подменяет ваш сервер. Схема, порты и health: Веб-приложения и домены, домены и порты.

Как читать getWebhookInfo

Откройте в браузере:
https://api.telegram.org/bot<ТОКЕН>/getWebhookInfo
Смотрите четыре поля.
ПолеЧто означает
urlКуда Telegram шлёт апдейты. Пустая строка: webhook снят, бот может работать через polling
last_error_messageПоследняя неудачная доставка. Пустое поле при заполненном url и стабильном pending_update_count обычно значит, что доставка проходит
pending_update_countСколько апдейтов ещё не принято. Растёт, если сервер не отвечает 200
last_error_dateМомент последней ошибки. Сверьте с временем вашего деплоя
Дальше действуйте по тексту last_error_message.

webhook 404 Not Found

Как выглядит

Wrong response from the webhook: 404 Not Found
Иногда тело ответа FastAPI: {"detail":"Not Found"}. Сертификат при этом валидный, браузер открывает https://домен/health.

Почему

TLS и прокси до контейнера уже работают. Внутри контейнера нет обработчика на тот путь, который записан в setWebhook. Три типичных расхождения на Bothost:
    • В Telegram указан https://домен/webhook, а приложение слушает / , /telegram или /bot.
    • Домен включён, своего HTTP-сервера в коде нет, отвечает служебная обёртка. У неё есть только GET /health. POST /webhook для неё неизвестный путь, отсюда 404. Бот как процесс может быть жив и даже отвечать через polling, если webhook при этом не снят: тогда часть апдейтов зависает в pending_update_count.
    • GET / в браузере тоже 404. Для многих ботов это нормально: страницы на корне нет. Ошибка доставки появляется, когда тот же 404 приходит на путь из url в ответ на POST.

Что сделать

    • Откройте https://домен/health. Если не открывается, это ещё не 404 маршрута, а порт или процесс: раздел про таймаут ниже и справочник ошибок.
    • Сверьте путь посимвольно: getWebhookInfo → url и декоратор в коде (/webhook, а не /webhook/).
    • Если health открывается, а своего сервера в репозитории нет, webhook на URL обёртки работать не будет. Либо реализуйте POST-обработчик в приложении и слушайте 0.0.0.0:PORT, либо снимите webhook и оставьте polling:
https://api.telegram.org/bot<ТОКЕН>/deleteWebhook
    • После смены пути вызовите setWebhook ещё раз и отправьте боту сообщение. pending_update_count должен перестать расти, в логах контейнера появится входящий POST.
Пример установки URL (секрет лучше задавать, Telegram пришлёт его в заголовке X-Telegram-Bot-Api-Secret-Token):
https://api.telegram.org/bot<ТОКЕН>/setWebhook?url=https://<домен>/webhook&secret_token=<секрет>

webhook SSL error

Как выглядит

В last_error_message есть SSL error или certificate verify failed. Реже: Wrong response from the webhook с кодом, который относится к рукопожатию, а не к вашему хендлеру. Браузер при открытии домена показывает предупреждение сертификата.

Почему Telegram отклоняет URL

Webhook принимается только по HTTPS с сертификатом, которому Telegram доверяет. Self-signed сертификат, голый http://, чужое имя в сертификате и домен, для которого сертификат ещё не выпущен, дают SSL error. На *.bothost.tech сертификат выпускает платформа после того, как домен назначен боту. Пока выпуск не закончился, setWebhook уже может сохранить URL, а доставка будет падать. Имя в URL должно совпадать с именем в панели, включая замену _ на -. Свой домен начинает работать после DNS-записи на IP, который выдаёт поддержка, и после выпуска сертификата. До этого момента Telegram видит SSL error, даже если контейнер уже running.

Что сделать

    • Откройте https://домен/health в браузере в режиме инкогнито. Страница должна открыться без предупреждения о сертификате.
    • В setWebhook используйте https:// и хост из панели, без порта :443 в обход и без IP вместо имени.
    • Если сертификат только что выпустили, вызовите setWebhook повторно после успешного открытия health в браузере.
    • Поле has_custom_certificate в getWebhookInfo для обычного домена Bothost должно быть false: свой сертификат в setWebhook загружать не нужно.

Connection timed out, Connection refused, 502

Как выглядит

Connection timed out
Connection refused
Wrong response from the webhook: 502 Bad Gateway
https://домен/health в браузере не открывается или долго крутится. Контейнер при этом restarting, exited или running.

Почему

Запрос не доходит до обработчика. На Bothost так бывает, когда процесс слушает 127.0.0.1 вместо 0.0.0.0, порт в коде зашит (например 8080), а в панели и в переменной PORT другое значение, либо процесс падает раньше, чем успевает занять порт. 502 и 504 со стороны прокси означают то же: на внутреннем порту никто не ответил вовремя. Слушать нужно так:
import os

host = "0.0.0.0"
port = int(os.environ.get("PORT", "3000"))
В логах работы после старта должна быть строка с этим хостом и портом. 127.0.0.1 с хоста платформы недоступен, webhook до него не дойдёт, хотя на вашем компьютере localhost открывается. Если контейнер в цикле перезапуска, сначала поднимите процесс: локально работает, на сервере падает.

pending_update_count растёт, last_error_message пустой или про таймаут ответа

Запрос до вашего кода доходит, но Telegram не считает доставку успешной. Причины:
  • обработчик не возвращает HTTP 200 (возвращает 500, обрывает соединение, ждёт долгую операцию и не отвечает);
  • ответ уходит слишком поздно, Telegram повторяет тот же update_id, в чате сообщения двоятся.
Ответьте 200 сразу, тяжёлую работу сделайте после ответа. Повторы обрабатывайте по update_id, чтобы второй приход того же апдейта не слал второе сообщение. Подробнее про таймаут хендлера: память, timeout и лимиты. Если url указывает на старый домен (бот переехал, поддомен сменился), Telegram продолжает биться в прежний адрес. Вызовите setWebhook с актуальным хостом из панели или deleteWebhook, если переходите на polling.

Короткий порядок проверки

    • Тариф с доменом, в панели у бота есть https://....
    • getMe возвращает ok: true (токен живой).
    • https://домен/health открывается в браузере без предупреждения сертификата.
    • getWebhookInfo: url совпадает с путём в коде, last_error_message пустой после тестового сообщения.
    • В логах контейнера есть входящий POST и нет цикла restarting.
    • В коде нет одновременно set_webhook и start_polling.
Тестовый проект с рабочим приёмом webhook: bothost-tech/bothost_testwebhook.

См. также

Не нашли свою строку в getWebhookInfo? Пришлите last_error_message в поддержку: @bothostru.