Telegram webhook: как настроить setWebhook для бота

Обновлено 19 октября 2026 г. · 10 мин чтения

Telegram webhook — это адрес на вашем сервере, на который Telegram сам отправляет новые события бота: сообщения, нажатия кнопок, добавление в группы. Вместо того чтобы постоянно спрашивать «есть что-нибудь новое?», бот получает данные мгновенно. Разберём, как установить webhook методом setWebhook, как защитить его секретным токеном, как написать обработчик и что делать, если события не доходят.

getUpdates или webhook: в чём разница

Бот получает события одним из двух способов, и они взаимоисключающие.

  • getUpdates (long polling). Ваш код сам запрашивает новые события и держит соединение открытым, пока они не появятся. Подтверждение получения — параметр offset, равный update_id последнего обработанного события плюс один. Подходит для разработки и небольших ботов: не нужны домен и сертификат.
  • Webhook. Telegram отправляет каждое событие POST-запросом с JSON на ваш HTTPS-адрес. Подходит для продакшена: не нужен постоянно работающий процесс-опросчик, события приходят сразу.

Пока webhook установлен, getUpdates не работает: Telegram вернёт ошибку 409 Conflict с подсказкой удалить webhook. И наоборот, чтобы перейти на webhook, достаточно его установить — опрос через getUpdates придётся остановить.

Если ни один способ не забирает события, Telegram хранит их на своей стороне, но не дольше 24 часов. После долгого простоя часть событий будет потеряна.

Переход с getUpdates на webhook на работающем боте лучше делать по шагам, чтобы не потерять сообщения пользователей:

  1. Разверните обработчик и убедитесь, что адрес открывается по HTTPS снаружи, а не только с самого сервера.
  2. Остановите процесс, который опрашивает getUpdates, — иначе он будет получать ошибки 409 и мешать отладке.
  3. Вызовите setWebhook без drop_pending_updates: события, накопившиеся за время переключения, придут на новый адрес.
  4. Отправьте боту тестовое сообщение и проверьте getWebhookInfo: pending_update_count должен быть равен нулю, а поле last_error_message — отсутствовать.

Требования к адресу webhook

  • Только HTTPS. Обычный HTTP для webhook официального Bot API не поддерживается, на каком бы порту ни работал сервер.
  • Допустимые порты: 443, 80, 88 и 8443. Другие порты не работают.
  • TLS 1.2 или новее. Более старые версии протокола не поддерживаются.
  • Валидный сертификат: CN или SAN должны совпадать с доменом, а сервер должен отдавать всю цепочку промежуточных сертификатов. Неполная цепочка — самая частая причина ошибок SSL при рабочем в браузере сайте.
  • Самоподписанный сертификат допустим, но его публичную часть нужно загрузить в параметре certificate при вызове setWebhook.
  • Telegram подключается к webhook по IPv4 — у домена должна быть A-запись, одного IPv6-адреса недостаточно.

Поскольку портов несколько, на одном сервере можно держать несколько ботов: каждому — свой путь, например /tg/leads и /tg/support, или свой порт из списка.

setWebhook: параметры и пример запроса

Webhook устанавливается одним запросом к Bot API. Удобно передавать параметры в JSON.

cURL: установка webhook
curl -X POST "https://api.telegram.org/bot<TOKEN>/setWebhook" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.ru/tg/webhook",
    "secret_token": "k3J9_sQ-2xVn81pLm7Rt",
    "allowed_updates": ["message", "callback_query"],
    "max_connections": 40,
    "drop_pending_updates": true
  }'

{"ok":true,"result":true,"description":"Webhook was set"}
  • url — HTTPS-адрес обработчика. Пустая строка в url удаляет webhook.
  • secret_token — строка от 1 до 256 символов: латинские буквы, цифры, подчёркивание и дефис. Telegram будет передавать её в заголовке X-Telegram-Bot-Api-Secret-Token каждого запроса. По нему обработчик отличает настоящие события от поддельных — адрес webhook рано или поздно может стать известен посторонним.
  • allowed_updates — список типов событий, которые нужны боту. Для кнопок под сообщениями обязательно укажите callback_query. Если параметр не передать, сохранится предыдущая настройка.
  • max_connections — сколько одновременных соединений Telegram может открыть к обработчику, от 1 до 100, по умолчанию 40. На слабом сервере имеет смысл уменьшить.
  • drop_pending_updates — сбросить накопившиеся события. Полезно при смене обработчика, чтобы бот не ответил на сообщения суточной давности.
  • certificate — публичный ключ самоподписанного сертификата в формате PEM.
Самоподписанный сертификат: загрузка вместе с адресом
curl -F "url=https://203.0.113.10:8443/tg/webhook" \
  -F "certificate=@public.pem" \
  -F "secret_token=k3J9_sQ-2xVn81pLm7Rt" \
  "https://api.telegram.org/bot<TOKEN>/setWebhook"

Секретный токен генерируйте случайно и храните в переменной окружения рядом с токеном бота. Это разные значения: токен бота нужен вам для вызова API, secret_token — Telegram для подписи запросов к вам.

Обработчик: проверить секрет и быстро ответить 200

Telegram считает событие доставленным, только если обработчик ответил кодом 2xx. При ошибке или таймауте он повторит отправку, а новые события будут копиться в очереди. Отсюда три правила.

  1. Проверяйте заголовок X-Telegram-Bot-Api-Secret-Token и отклоняйте запросы с неверным значением.
  2. Отвечайте 200 как можно быстрее. Долгую работу — запросы к базе, CRM, отправку писем — выполняйте после ответа или через очередь задач.
  3. Делайте обработку идемпотентной. Одно и то же событие может прийти повторно, если ответ не дошёл до Telegram. Запоминайте update_id обработанных событий и пропускайте повторы.
Минимальный обработчик на Node.js 18+ без зависимостей
const http = require('node:http')

const SECRET = process.env.TG_WEBHOOK_SECRET
const TOKEN = process.env.TG_TOKEN
const seen = new Set() // в продакшене — база или Redis

http.createServer((req, res) => {
  if (req.method !== 'POST' || req.url !== '/tg/webhook') {
    res.writeHead(404).end()
    return
  }
  if (req.headers['x-telegram-bot-api-secret-token'] !== SECRET) {
    res.writeHead(401).end()
    return
  }
  let body = ''
  req.on('data', (chunk) => { body += chunk })
  req.on('end', () => {
    res.writeHead(200).end() // отвечаем сразу, обрабатываем потом
    let update
    try { update = JSON.parse(body) } catch { return }
    if (seen.has(update.update_id)) return
    seen.add(update.update_id)
    handleUpdate(update).catch(console.error)
  })
}).listen(3000)

async function handleUpdate(update) {
  const msg = update.message
  if (!msg || !msg.text) return
  await fetch('https://api.telegram.org/bot' + TOKEN + '/sendMessage', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: msg.chat.id, text: 'Получено: ' + msg.text }),
  })
}

Сервер слушает порт 3000, а HTTPS и порт 443 обеспечивает обратный прокси (nginx, Caddy или панель хостинга) — так проще обновлять сертификат. В этом примере событие подтверждается до обработки: если процесс упадёт в этот момент, событие потеряется. Для важных данных сначала записывайте событие в базу или очередь задач и только потом отвечайте 200.

Есть и ещё одна возможность: в теле ответа на webhook можно сразу вызвать метод Bot API — например, вернуть JSON с полем method: "sendMessage" и параметрами сообщения. Это экономит один запрос, но результат такого вызова вы не узнаете, поэтому для важных сообщений надёжнее отдельный запрос.

Диагностика: getWebhookInfo, deleteWebhook и локальная отладка

Первое, что нужно сделать, если бот «молчит», — вызвать getWebhookInfo. Метод показывает текущий адрес, число недоставленных событий и последнюю ошибку.

Ответ getWebhookInfo при проблемах с обработчиком
https://api.telegram.org/bot<TOKEN>/getWebhookInfo

{"ok":true,"result":{
  "url":"https://example.ru/tg/webhook",
  "has_custom_certificate":false,
  "pending_update_count":37,
  "last_error_date":1792400000,
  "last_error_message":"Wrong response from the webhook: 502 Bad Gateway",
  "max_connections":40,
  "ip_address":"203.0.113.10"}}
  • pending_update_count растёт — обработчик не принимает события, Telegram копит их и повторяет попытки.
  • last_error_message — текст последней ошибки: код ответа обработчика, таймаут соединения или проблема с SSL.
  • last_error_date — время ошибки в формате Unix. Если оно давнее, а очередь пуста, проблема уже решена.
  • ip_address — IP, по которому Telegram обращается к серверу. Помогает заметить, что DNS указывает не туда.

Метод deleteWebhook снимает webhook и возвращает бота к getUpdates. С параметром drop_pending_updates=true заодно очищается очередь накопившихся событий.

Для локальной разработки нужен публичный HTTPS-адрес, который ведёт на ваш компьютер. Его дают сервисы туннелей: они выдают временный домен с сертификатом и пересылают запросы на localhost. Установите этот адрес через setWebhook, а после отладки верните рабочий. Надёжнее завести для разработки отдельного тестового бота: у одного бота может быть только один webhook, и эксперименты с рабочим ботом перехватят события у продакшена.

Частые ошибки при настройке webhook

  • 409 Conflict при вызове getUpdates — webhook ещё установлен. Удалите его через deleteWebhook или продолжайте работать через webhook.
  • Ошибки SSL в last_error_message — сервер не отдаёт промежуточные сертификаты, сертификат выписан на другой домен или используется устаревшая версия TLS.
  • Connection timed out или connection refused — порт не из списка 443, 80, 88, 8443, закрыт файрволом, либо домен указывает на IPv6 без IPv4.
  • Wrong response from the webhook: 404 — неверный путь обработчика или прокси не пропускает POST-запросы на этот адрес.
  • Wrong response from the webhook: 401 или 403 — обработчик отклоняет запросы: secret_token в setWebhook не совпадает со значением в коде, или запрос режет защита хостинга от ботов.
  • Бот отвечает на одно сообщение несколько раз — обработчик отвечает медленнее, чем ждёт Telegram, и событие приходит повторно. Отвечайте сразу и проверяйте update_id.
  • Не приходят нажатия кнопок — в allowed_updates нет callback_query.
  • Webhook «сам» сменился — тот же токен используется в другом сервисе или скрипте, который вызвал setWebhook со своим адресом.

Последний пункт важен, если бот подключён к внешним сервисам. Например, в Site-Inbox webhook для кнопок модерации комментариев («Одобрить» и «Отклонить») регистрируется автоматически при сохранении Telegram-коннектора — но только если у бота нет другого webhook. Чужой адрес сервис не перезаписывает, поэтому для кнопок модерации удобнее использовать отдельного бота, а не того, у которого уже есть свой обработчик.

Частые вопросы

Можно ли использовать webhook и getUpdates одновременно?

Нет. Пока webhook установлен, getUpdates возвращает ошибку 409 Conflict. Чтобы вернуться к опросу, вызовите deleteWebhook.

Можно ли установить два webhook для одного бота?

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

Нужен ли домен для webhook?

Для обычного сертификата — да: имя в сертификате должно совпадать с доменом. С самоподписанным сертификатом можно использовать IP-адрес, загрузив публичный ключ в параметре certificate.

Как проверить, что запрос пришёл от Telegram?

Передайте secret_token в setWebhook и сравнивайте его с заголовком X-Telegram-Bot-Api-Secret-Token в каждом запросе. Запросы без заголовка или с другим значением отклоняйте.

Ведите переписку из Telegram-ботов ваших проектов в одной панели: подключите своего бота к Site-Inbox и отвечайте клиентам из одного окна.

Читайте также