Разработка Telegram-бота локально часто упирается не только в localhost. Во многих реальных сетях компьютер разработчика вообще недоступен из интернета: он находится за домашним роутером, корпоративным фаерволом, CGNAT провайдера, мобильной сетью или VPN.
Даже если на компьютере уже запущен HTTP-сервер на http://127.0.0.1:3000, Telegram не сможет обратиться к нему напрямую. Для webhook Telegram нужен публичный HTTPS URL, доступный извне. Обычно это означает необходимость получить публичный IP-адрес, открыть входящий порт на фаерволе, настроить NAT или port forwarding на роутере, зарегистрировать домен и выпустить TLS-сертификат.
Для локальной разработки всё это избыточно. В этой инструкции Telegram будет отправлять обновления на сервер Adal, а Adal доставит их в локальный обработчик через исходящее соединение Adal CLI. Поэтому компьютер может оставаться за NAT и фаерволом: публичный IP, открытые входящие порты и собственный HTTPS-сервер не потребуются.
Что получится в итоге
Схема будет выглядеть так:
Пользователь Telegram
↓
Telegram Bot API
↓ HTTPS POST
Публичный сервер в Adal
↓
Исходящее соединение Adal CLI
↓
Компьютер разработчика за NAT / фаерволом
↓
http://127.0.0.1:3000/telegram
Telegram взаимодействует только с публичным HTTPS-сервером в Adal. При этом ваш компьютер не принимает входящие подключения из интернета: Adal CLI сам устанавливает исходящее соединение с Adal и получает через него webhook-запросы для локальной Destination.
Это работает в том числе когда:
компьютер подключён к домашнему Wi-Fi за роутером;
провайдер использует CGNAT и не выдаёт публичный IPv4-адрес;
на рабочем компьютере запрещено открывать входящие порты;
приложение запущено внутри локального Docker-контейнера;
вы работаете через VPN, мобильный интернет или закрытую корпоративную сеть.
Почему NAT и фаервол мешают Telegram webhook
Когда Telegram отправляет webhook, он должен сам открыть соединение с вашим сервером по HTTPS. Для этого сервер должен быть доступен из интернета по публичному адресу.
Но локальный компьютер обычно находится во внутренней сети:
Telegram
↓
Публичный IP роутера или провайдера
↓
NAT / фаервол
↓
Ваш локальный компьютер: 192.168.x.x или 10.x.x.x
Адреса вроде 192.168.x.x, 10.x.x.x, 172.16.x.x и 127.0.0.1 не маршрутизируются из интернета. Даже если вы знаете внешний IP роутера, входящее соединение может быть заблокировано фаерволом или не попасть на нужный компьютер без явной настройки NAT.
Adal меняет направление соединения. Вместо того чтобы Telegram подключался к вашему компьютеру, ваш компьютер сам подключается к Adal:
Ваш компьютер → Adal CLI → Adal
Telegram → публичный сервер Adal
Исходящие HTTPS- и WebSocket-соединения обычно разрешены даже в сетях, где входящие подключения заблокированы. За счёт этого локальная машина может получать Telegram webhooks, оставаясь полностью закрытой для прямого доступа из интернета.
Что потребуется
Для настройки нужны:
Telegram-бот, созданный через
@BotFather;токен бота;
аккаунт Adal;
установленный и авторизованный Adal CLI;
локальный HTTP-сервер, который принимает
POST-запросы;Node.js, PHP, Python или другой runtime для вашего приложения.
В примере ниже будет использоваться Node.js, но способ подключения не зависит от языка и фреймворка.
1. Создайте Telegram-бота и сохраните токен
Откройте @BotFather в Telegram и выполните команду:
/newbot
BotFather попросит указать имя и username бота, после чего выдаст токен вида:
123456789:AAExampleTokenThatMustRemainPrivate
Не добавляйте токен в Git, не отправляйте его в сообщения и не вставляйте в frontend-код. Токен даёт полный доступ к управлению ботом через Telegram Bot API.
Для локальной разработки сохраните его в переменную окружения:
export TELEGRAM_BOT_TOKEN='123456789:AAExampleTokenThatMustRemainPrivate'
На Windows PowerShell:
$env:TELEGRAM_BOT_TOKEN = '123456789:AAExampleTokenThatMustRemainPrivate'
2. Запустите локальный обработчик Telegram webhook
Создайте файл server.mjs:
import http from 'node:http';
const port = 3000;
const webhookSecret = process.env.TELEGRAM_WEBHOOK_SECRET;
const server = http.createServer(async (request, response) => {
if (request.method !== 'POST' || request.url !== '/telegram') {
response.writeHead(404);
response.end('Not found');
return;
}
const receivedSecret =
request.headers['x-telegram-bot-api-secret-token'];
if (!webhookSecret || receivedSecret !== webhookSecret) {
response.writeHead(401);
response.end('Unauthorized');
return;
}
let body = '';
request.on('data', (chunk) => {
body += chunk;
});
request.on('end', () => {
try {
const update = JSON.parse(body);
console.log('Telegram update received:');
console.dir(update, { depth: null });
const message = update.message;
if (message?.text) {
console.log(
`Message from ${message.from.username ?? message.from.id}: ${message.text}`
);
}
response.writeHead(200, {
'content-type': 'application/json',
});
response.end(JSON.stringify({ ok: true }));
} catch (error) {
console.error('Failed to process Telegram update:', error);
response.writeHead(400, {
'content-type': 'application/json',
});
response.end(JSON.stringify({ ok: false }));
}
});
});
server.listen(port, '127.0.0.1', () => {
console.log(`Listening on http://127.0.0.1:${port}/telegram`);
});
Сгенерируйте отдельный секрет для проверки запросов Telegram:
openssl rand -base64 32 | tr '+/' '-_' | tr -d '='
Сохраните результат в переменную окружения:
export TELEGRAM_WEBHOOK_SECRET='your-random-secret'
После этого запустите сервер:
node server.mjs
В консоли должно появиться:
Listening on http://127.0.0.1:3000/telegram
Локальный сервер уже готов принимать Telegram updates, но пока он доступен только на вашем компьютере. Следующим шагом нужно создать публичную точку приёма в Adal.
3. Создайте сервер для Telegram в Adal
Откройте Dashboard Adal и создайте новый сервер.
Для него рекомендуется указать следующие настройки:
Name:
telegram-local;Allowed methods:
POST;Deliver pending requests on connect: включено;
Notes:
Telegram Bot API updates for local development.
После создания Adal выдаст серверу постоянный публичный URL. Он будет выглядеть примерно так:
https://your-server.se1.adal.cloud
Это URL, который будет указан в Telegram Bot API как webhook URL.
Не добавляйте к нему локальный путь вроде /telegram, пока не убедитесь, как именно настроена маршрутизация в вашей Destination. В этой инструкции внешний URL сервера остаётся корневым, а путь /telegram используется внутри локального приложения.
Также, при создании сервера, будет выдан CLI Token, который нужно сохранить, так как он отображается только один раз. Если вы его потеряете, то восстановить его не получится — только сгенерировать новый.
4. Создайте локальную Destination через Adal CLI
Внутри сервера создайте Destination, которая будет доставлять запросы через Adal CLI на локальный адрес:
http://127.0.0.1:3000/telegram
Создайте CLI token для этой Destination и подключите локальный клиент Adal. Используйте команду подключения, которую показывает Dashboard после создания токена.
После успешного подключения Destination должна перейти в состояние online.
На этом этапе Adal CLI устанавливает исходящее соединение с Adal. Это принципиально важно: Telegram и другие внешние сервисы не подключаются к вашему компьютеру напрямую.
Ваш компьютер может находиться за NAT, домашним роутером, корпоративным фаерволом, VPN или CGNAT. Ему не нужен публичный IP-адрес, проброс портов, открытый входящий порт, домен или собственный TLS-сертификат. Для работы достаточно, чтобы компьютер мог устанавливать исходящие подключения к Adal.
5. Установите webhook в Telegram
Создайте переменную с URL сервера Adal:
export ADAL_SERVER_URL='https://your-server.se1.adal.cloud'
Затем вызовите метод setWebhook:
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
-d "url=${ADAL_SERVER_URL}" \
-d 'allowed_updates=["message","callback_query"]' \
-d "secret_token=${TELEGRAM_WEBHOOK_SECRET}"
При успешной настройке Telegram вернёт ответ:
{
"ok": true,
"result": true,
"description": "Webhook was set"
}
Параметр secret_token особенно важен. Telegram будет добавлять его в каждый запрос в заголовке:
X-Telegram-Bot-Api-Secret-Token
Локальный обработчик из примера выше проверяет этот заголовок и отклоняет запросы без корректного секрета.
6. Проверьте, что webhook работает
Откройте чат с ботом и отправьте ему сообщение:
/start
В консоли локального приложения должен появиться объект Telegram update:
Telegram update received:
{
update_id: 123456789,
message: {
message_id: 1,
from: {
id: 12345678,
is_bot: false,
first_name: "Yuriy"
},
chat: {
id: 12345678,
type: "private"
},
date: 1782921600,
text: "/start"
}
}
Одновременно в Dashboard Adal появится новый принятый webhook и информация о его доставке в локальную Destination.
Это удобно не только для настройки, но и для отладки: можно посмотреть заголовки, тело запроса, статус доставки и при необходимости повторно отправить конкретный update в локальное приложение.
Как проверить текущую настройку Telegram webhook
Telegram позволяет запросить текущий статус webhook через метод getWebhookInfo:
curl -sS \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
Пример успешного ответа:
{
"ok": true,
"result": {
"url": "https://your-server.se1.adal.cloud",
"has_custom_certificate": false,
"pending_update_count": 0,
"max_connections": 40,
"allowed_updates": [
"message",
"callback_query"
]
}
}
Обратите внимание на следующие поля:
url— адрес, на который Telegram сейчас отправляет updates;pending_update_count— количество обновлений, ожидающих доставки;last_error_message— последняя ошибка при доставке;last_error_date— время последней ошибки;allowed_updates— типы обновлений, которые бот получает.
Если поле url пустое, значит webhook не установлен и бот работает через long polling либо не получает обновления вовсе.
Как получать Telegram webhooks за NAT и фаерволом
Адреса localhost, 127.0.0.1 и ::1 существуют только внутри компьютера, на котором запущено приложение. Для Telegram localhost не указывает на ваш MacBook, рабочий ноутбук или Docker-контейнер — он указывает на инфраструктуру Telegram.
Но проблема не ограничивается localhost. Даже сервер, доступный в домашней сети по адресу 192.168.1.10, нельзя вызвать из интернета напрямую. Такие адреса находятся за NAT роутера и не маршрутизируются в публичной сети.
Чтобы Telegram мог обращаться к локальному компьютеру без Adal, обычно пришлось бы:
получить публичный IP-адрес;
настроить port forwarding на роутере;
открыть входящий порт в фаерволе;
настроить публичный DNS-адрес;
выпустить и обслуживать TLS-сертификат;
следить, чтобы адрес и правила сети не изменились.
В корпоративной сети, при CGNAT или на мобильном интернете часть этих действий может быть невозможна в принципе.
С Adal эта инфраструктура не нужна. Telegram отправляет webhook на публичный HTTPS-сервер Adal, а Adal передаёт запрос через уже установленное исходящее соединение Adal CLI в ваше локальное приложение.
Последовательность доставки выглядит так:
Telegram отправляет update на публичный HTTPS-сервер в Adal.
Adal принимает и сохраняет входящий webhook.
Adal создаёт доставку для подключённой локальной Destination.
Adal CLI получает эту доставку через исходящее соединение с вашего компьютера.
CLI передаёт HTTP-запрос на
http://127.0.0.1:3000/telegram.Ваш обработчик отвечает так, как если бы Telegram подключился к нему напрямую.
В результате Telegram webhook может работать на компьютере за NAT и фаерволом без открытия каких-либо входящих портов.
Что произойдёт, если локальный компьютер выключен
Telegram продолжит отправлять updates на сервер Adal, потому что публичный URL остаётся доступным независимо от состояния вашего ноутбука.
Когда локальная Destination недоступна, доставка не пропадает мгновенно. В Adal она фиксируется в истории и обрабатывается в соответствии с настройками повторных попыток. После повторного подключения Adal CLI можно доставить ожидающие webhook-запросы в локальное приложение.
Для разработки это особенно удобно: можно закрыть ноутбук, вернуться к задаче позже, подключить CLI и увидеть, какие updates пришли за время отсутствия.
При этом важно учитывать бизнес-логику бота. Telegram updates могут быть доставлены повторно, поэтому обработчик должен быть идемпотентным. Практический минимум — хранить update_id и не выполнять одно и то же действие дважды.
Как отключить Telegram webhook
Если нужно вернуться к long polling через getUpdates, сначала удалите webhook:
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/deleteWebhook"
Чтобы одновременно удалить накопившиеся обновления, передайте параметр drop_pending_updates:
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/deleteWebhook" \
-d 'drop_pending_updates=true'
Используйте этот параметр осознанно: он удаляет необработанные updates на стороне Telegram.
Частые проблемы
Telegram возвращает 409 Conflict
Обычно это означает, что приложение пытается использовать getUpdates, пока webhook уже установлен.
Webhook и long polling нельзя использовать одновременно для одного бота. Проверьте текущую конфигурацию:
curl -sS \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
Если url не пустой, отключите polling в приложении или удалите webhook.
В Adal webhook принят, но локальное приложение ничего не получает
Проверьте три вещи:
Adal CLI подключён и Destination находится в состоянии online.
Локальный сервер слушает именно
127.0.0.1:3000.В Destination указан правильный адрес:
http://127.0.0.1:3000/telegram
Также проверьте историю доставок в Adal. Она покажет HTTP-статус, ошибку соединения или таймаут локального обработчика.
Локальное приложение отвечает 401 Unauthorized
Это означает, что не совпадает TELEGRAM_WEBHOOK_SECRET.
Проверьте, что одно и то же значение используется:
в переменной окружения локального приложения;
в параметре
secret_tokenпри вызовеsetWebhook.
После изменения секрета повторно вызовите setWebhook, чтобы Telegram начал отправлять новый заголовок.
Telegram показывает ошибку доставки webhook
Запросите информацию через getWebhookInfo и посмотрите поле last_error_message.
Если сервер Adal принимает запросы, а локальная доставка не работает, ошибка обычно будет видна в истории конкретного webhook в Adal. Это позволяет разделить проблему на две части: доступность Telegram → Adal и доставка Adal → localhost.
Итог
Telegram Bot API требует публичный HTTPS URL для webhook-доставки, но ваш локальный компьютер не обязан быть публично доступным.
С помощью Adal можно получать Telegram webhooks на машину за NAT, домашним роутером, корпоративным фаерволом, CGNAT, VPN или мобильным интернетом. Не нужны публичный IP-адрес, проброс портов, настройка входящего firewall rule, домен или TLS-сертификат на локальной машине.
Создайте сервер в Adal, подключите локальную Destination через Adal CLI и укажите URL сервера в setWebhook. Telegram будет отправлять обновления в Adal, а ваше приложение будет получать их на localhost через исходящее соединение.
Такой подход позволяет разрабатывать и отлаживать Telegram-ботов локально в закрытой сети, не меняя сетевую конфигурацию компьютера и не открывая его для прямого доступа из интернета.