<!-- Bothost docs | HTML: https://bothost.ru/docs/telegram-webhook-errors | Markdown: https://bothost.ru/docs/telegram-webhook-errors.md | Index: https://bothost.ru/llms.txt -->

# Webhook Telegram не работает: 404, SSL error, апдейты не приходят

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`: [Веб-приложения и домены](web-apps-domains). Если в логе Conflict и бот на polling, откройте [бот не отвечает](bot-not-responding).

---

## Что 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: [Веб-приложения и домены](web-apps-domains), [домены и порты](domains-and-ports).

---

## Как читать getWebhookInfo

Откройте в браузере:

```text
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

### Как выглядит

```text
Wrong response from the webhook: 404 Not Found
```

Иногда тело ответа FastAPI: `{"detail":"Not Found"}`. Сертификат при этом валидный, браузер открывает `https://домен/health`.

### Почему

TLS и прокси до контейнера уже работают. Внутри контейнера нет обработчика на тот путь, который записан в `setWebhook`.

Три типичных расхождения на Bothost:

1. В Telegram указан `https://домен/webhook`, а приложение слушает `/` , `/telegram` или `/bot`.
2. Домен включён, своего HTTP-сервера в коде нет, отвечает служебная обёртка. У неё есть только `GET /health`. `POST /webhook` для неё неизвестный путь, отсюда 404. Бот как процесс может быть жив и даже отвечать через polling, если webhook при этом не снят: тогда часть апдейтов зависает в `pending_update_count`.
3. `GET /` в браузере тоже 404. Для многих ботов это нормально: страницы на корне нет. Ошибка доставки появляется, когда **тот же** 404 приходит на путь из `url` в ответ на POST.

### Что сделать

1. Откройте `https://домен/health`. Если не открывается, это ещё не 404 маршрута, а порт или процесс: раздел про таймаут ниже и [справочник ошибок](common-errors).
2. Сверьте путь посимвольно: `getWebhookInfo` → `url` и декоратор в коде (`/webhook`, а не `/webhook/`).
3. Если health открывается, а своего сервера в репозитории нет, webhook на URL обёртки работать не будет. Либо реализуйте POST-обработчик в приложении и слушайте `0.0.0.0`:`PORT`, либо снимите webhook и оставьте polling:

```text
https://api.telegram.org/bot<ТОКЕН>/deleteWebhook
```

4. После смены пути вызовите `setWebhook` ещё раз и отправьте боту сообщение. `pending_update_count` должен перестать расти, в логах контейнера появится входящий POST.

Пример установки URL (секрет лучше задавать, Telegram пришлёт его в заголовке `X-Telegram-Bot-Api-Secret-Token`):

```text
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`.

### Что сделать

1. Откройте `https://домен/health` в браузере в режиме инкогнито. Страница должна открыться без предупреждения о сертификате.
2. В `setWebhook` используйте `https://` и хост из панели, без порта `:443` в обход и без IP вместо имени.
3. Если сертификат только что выпустили, вызовите `setWebhook` повторно после успешного открытия health в браузере.
4. Поле `has_custom_certificate` в `getWebhookInfo` для обычного домена Bothost должно быть `false`: свой сертификат в `setWebhook` загружать не нужно.

---

## Connection timed out, Connection refused, 502

### Как выглядит

```text
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 со стороны прокси означают то же: на внутреннем порту никто не ответил вовремя.

Слушать нужно так:

```python
import os

host = "0.0.0.0"
port = int(os.environ.get("PORT", "3000"))
```

В логах работы после старта должна быть строка с этим хостом и портом. `127.0.0.1` с хоста платформы недоступен, webhook до него не дойдёт, хотя на вашем компьютере `localhost` открывается.

Если контейнер в цикле перезапуска, сначала поднимите процесс: [локально работает, на сервере падает](works-locally-fails-on-server).

---

## pending_update_count растёт, last_error_message пустой или про таймаут ответа

Запрос до вашего кода доходит, но Telegram не считает доставку успешной. Причины:

- обработчик не возвращает HTTP `200` (возвращает `500`, обрывает соединение, ждёт долгую операцию и не отвечает);
- ответ уходит слишком поздно, Telegram повторяет тот же `update_id`, в чате сообщения двоятся.

Ответьте `200` сразу, тяжёлую работу сделайте после ответа. Повторы обрабатывайте по `update_id`, чтобы второй приход того же апдейта не слал второе сообщение. Подробнее про таймаут хендлера: [память, timeout и лимиты](bot-memory-timeout-rate-limits).

Если `url` указывает на старый домен (бот переехал, поддомен сменился), Telegram продолжает биться в прежний адрес. Вызовите `setWebhook` с актуальным хостом из панели или `deleteWebhook`, если переходите на polling.

---

## Короткий порядок проверки

1. Тариф с доменом, в панели у бота есть `https://...`.
2. `getMe` возвращает `ok: true` (токен живой).
3. `https://домен/health` открывается в браузере без предупреждения сертификата.
4. `getWebhookInfo`: `url` совпадает с путём в коде, `last_error_message` пустой после тестового сообщения.
5. В логах контейнера есть входящий POST и нет цикла `restarting`.
6. В коде нет одновременно `set_webhook` и `start_polling`.

Тестовый проект с рабочим приёмом webhook: [bothost-tech/bothost_testwebhook](https://github.com/bothost-tech/bothost_testwebhook).

---

## См. также

- [Бот не отвечает: Conflict, токен, Discord](bot-not-responding)
- [Веб-приложения и домены](web-apps-domains)
- [Домены и порты](domains-and-ports)
- [Справочник ошибок при деплое](common-errors)

**Не нашли свою строку в getWebhookInfo?** Пришлите `last_error_message` в поддержку: [@bothostru](https://t.me/bothostru).
