Как настроить webhook Telegram-бота на localhost
Запустите обработчик Telegram Bot API на своём компьютере и направьте к нему обновления по публичному HTTPS-адресу. На тестовом боте вы проверите весь путь от сообщения в Telegram до ответа локального сервера.
Пошаговая настройка
-
Запустите локальный обработчик обновлений
Создайте отдельного тестового бота через @BotFather и сохраните его токен для третьего шага. В первом терминале Bash выполните пример с Python 3 и OpenSSL. Пример слушает только 127.0.0.1:3000, проверяет секретный заголовок и выводит номер обновления. Он подтверждает получение события и не отправляет сообщения в чат. Сохраните показанный секрет для регистрации webhook и держите его отдельно от токена бота.
export TELEGRAM_WEBHOOK_SECRET="$(openssl rand -hex 24)" printf 'Webhook secret: %s\n' "$TELEGRAM_WEBHOOK_SECRET" python3 -u - <<'PY' import hmac import json import os from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer secret = os.environ["TELEGRAM_WEBHOOK_SECRET"].encode() class Handler(BaseHTTPRequestHandler): def do_POST(self): if self.path != "/telegram": self.send_error(404) return supplied = self.headers.get("X-Telegram-Bot-Api-Secret-Token", "") if not hmac.compare_digest(supplied.encode(), secret): self.send_error(403) return try: size = int(self.headers.get("Content-Length", "0")) if not 0 < size <= 1_048_576: self.send_error(413) return update_id = json.loads(self.rfile.read(size))["update_id"] if type(update_id) is not int: raise ValueError("Invalid update ID") except (ValueError, KeyError, TypeError): self.send_error(400) return print(f"Received update_id={update_id}") self.send_response(200) self.end_headers() if not secret: raise SystemExit("Could not generate the webhook secret") ThreadingHTTPServer(("127.0.0.1", 3000), Handler).serve_forever() PY -
Откройте HTTPS-туннель к порту обработчика
Установите клиент ForTunnels и привяжите его к аккаунту CLI-токеном из настроек дашборда, чтобы видеть запросы в своём инспекторе. Во втором терминале запустите туннель. Скопируйте выданный HTTPS-адрес и оставьте оба процесса работать. Локальный обработчик продолжает слушать по HTTP. Telegram обращается к публичному HTTPS-входу.
fortunnels http 3000 -
Укажите публичный адрес в setWebhook
В третьем терминале Bash запустите команды. При запросе введите токен тестового бота, затем секрет, который показал первый терминал, и адрес из вывода ForTunnels без завершающего слэша. Команда добавит путь /telegram. Telegram должен вернуть JSON с ok и result, равными true. Используйте отдельного бота: setWebhook изменяет адрес доставки для этого токена, а активный webhook несовместим с получением обновлений через getUpdates.
read -rsp 'BotFather token: ' BOT_TOKEN printf '\n' read -rsp 'Webhook secret: ' TELEGRAM_WEBHOOK_SECRET printf '\n' read -rp 'Public HTTPS URL: ' TUNNEL_URL curl -fsS --config - <<CURL url = "https://api.telegram.org/bot$BOT_TOKEN/setWebhook" data-urlencode = "url=$TUNNEL_URL/telegram" data-urlencode = "secret_token=$TELEGRAM_WEBHOOK_SECRET" data-urlencode = "allowed_updates=[\"message\"]" CURL -
Отправьте сообщение и проверьте доставку
Откройте чат с тестовым ботом и отправьте ему сообщение, например «Проверка webhook». В дашборде откройте туннель и его инспектор. Найдите свежую строку POST /telegram и проверьте код 200. В первом терминале появится Received update_id с номером события. Если сообщений нет, команда getWebhookInfo покажет сохранённый адрес, число ожидающих обновлений и последнюю ошибку, если она была.
curl -fsS --config - <<CURL url = "https://api.telegram.org/bot$BOT_TOKEN/getWebhookInfo" CURL -
Снимите webhook перед остановкой туннеля
Удалите webhook у тестового бота, пока публичный адрес ещё активен. Команда очищает переменные третьего терминала после успешного ответа. Она не сбрасывает очередь ожидающих обновлений. Затем остановите туннель и обработчик клавишами Ctrl+C. Если продолжите работу в режиме опроса, запускайте его после удаления webhook.
curl -fsS --config - <<CURL && unset BOT_TOKEN TELEGRAM_WEBHOOK_SECRET TUNNEL_URL url = "https://api.telegram.org/bot$BOT_TOKEN/deleteWebhook" request = "POST" CURL
Инспектор: POST /telegram → 200
Локальный терминал: Received update_id=123456789
Что проверить по коду ответа
404 означает, что запрос попал не на тот путь или адрес. Сравните поле url в getWebhookInfo и строку в инспекторе. Сохраните полный HTTPS-адрес, который выдал клиент, и добавьте ровно один /telegram. Если туннель получил новый адрес после запуска, заново вызовите setWebhook и отправьте новое сообщение.
403 от обработчика означает несовпадение секрета. Проверьте, что secret_token при регистрации совпадает со значением, с которым запустили Python. При перезапуске примера OpenSSL создаёт новый секрет. Если инспектор не показывает тела, для этой проверки достаточно пути, кода ответа и локального журнала. Включать полный захват сообщений и секретных заголовков для пробы не требуется.
Если инспектор показывает 502, убедитесь, что первый терминал открыт и Python слушает порт 3000. Если в инспекторе вообще нет нового события, сначала сверьте URL в Telegram и состояние туннеля. Поле last_error_message может относиться к предыдущей попытке, поэтому сопоставьте время ошибки со временем отправки.
Перенесите проверку в своего бота
Сохраните проверку секретного заголовка и возвращайте успешный ответ после надёжного приёма обновления. Когда Telegram не получает успешного ответа, он может отправить обновление повторно. Если команда бота создаёт заявку или отправляет письмо, проверяйте update_id перед повторным применением действия. Отладку начните на тестовом боте с одним сообщением и сравните его номер в журнале с последующими попытками.
Разделите три секрета по назначению. Токен BotFather разрешает вызовы Bot API, секрет webhook проверяет входящие обновления, а токен ForTunnels привязывает CLI к вашему аккаунту. Для проверки доставки держите каждый секрет в своём процессе и не вставляйте их в задачи, скриншоты или общий журнал приложения.
Частые вопросы
Почему бот молчит, хотя в инспекторе появился код 200?
Код 200 подтверждает доставку обновления. Пример лишь печатает его идентификатор. Ответ в чате появится, когда обработчик вызовет метод sendMessage с идентификатором нужного чата.
Нужен ли Python, если бот написан на Node.js или Go?
Python нужен только для пробного обработчика. Запустите свой сервер, укажите его порт в команде туннеля, а путь реального POST-обработчика зарегистрируйте через setWebhook.
Источники
Запустите свой туннель
Зарегистрируйтесь, чтобы управлять маршрутами из дашборда.