Деплой бота — webhook vs polling и где хостить
Локально бот работает на длинном polling — раз в секунду спрашивает у Telegram «что нового?». На проде это работает, но плохо: лишний траффик, медленная реакция на сообщения, и ты обязан держать процесс живым 24/7. Webhook переворачивает схему: Telegram сам стучится к твоему серверу когда приходит сообщение. Это быстрее, дешевле, но требует HTTPS-эндпоинта. Разберёмся когда что использовать и как задеплоить.
Polling — простой, но не для прода
Long polling работает так: твой клиент делает запрос к Telegram getUpdates с большим timeout (~50 секунд). Telegram держит соединение открытым и отвечает как только появятся сообщения, или через 50 секунд если ничего не было. Плюсы: ничего не настраивать, работает за NAT, легко отлаживать локально. Минусы: один процесс должен быть живой ВСЕГДА, при перезапуске лагает 50 секунд, на пиковой нагрузке упирается в одно соединение. Подходит для прототипов и личных ботов.
Webhook — правильный путь для прода
Ты регистрируешь у Telegram URL вида
Когда приходит сообщение, Telegram сам делает POST на этот URL с update в теле. Плюсы: реакция мгновенная, бот может быть serverless (запускаться по запросу), масштабируется горизонтально. Требования: HTTPS обязательно, валидный сертификат (Let's Encrypt бесплатный), порт 80/443/88/8443. Регистрация через bot.api.setWebhook(url) один раз. После этого polling больше не работает (либо одно либо другое).
Где хостить — 4 варианта
(1) Свой VPS (DigitalOcean, Hetzner, Timeweb) — $4-6/мес, полный контроль, нужно настраивать nginx + systemd + Let's Encrypt. Хорошо для polling и для webhook. (2) Vercel — для serverless webhook на Next.js, бесплатно для небольшой нагрузки. POST на /api/webhook/route.ts обрабатывает каждое сообщение в отдельной функции. (3) Cloudflare Workers — глобальный edge, дёшево, низкая задержка. Идеально для webhook. (4) Railway/Render — managed VPS, чуть дороже DO но проще настроить (push в git → задеплоилось). Для большинства case-ов выбирай Cloudflare Workers (если знаком) или Railway (если хочешь чтоб just works).
Безопасность вебхука — секрет токен
Telegram даёт всему интернету твой webhook URL. Кто угодно может слать туда фейковые update и притворяться Telegram-ом. Защита: при setWebhook передаёшь secret_token. Telegram включает его в заголовок X-Telegram-Bot-Api-Secret-Token каждого запроса. Твой обработчик первым делом проверяет этот заголовок — если не совпадает, отвечает 401. Без этого любой может отправить тебе фейковый update и заставить бота сделать что-то от имени админа.
Anti-flood и масштаб
Telegram ограничения: 30 сообщений/сек на разных юзеров, 1 сообщ/сек на одного юзера, 20 сообщ/мин в один групповой чат. Если бьёшь лимит — получаешь 429 с retry_after. У grammY есть встроенный rate limiter, для самописа — exponential backoff. Под нагрузкой webhook + serverless = масштаб бесплатно. Polling = упрёшься в один процесс. Если бот популярный (100+ сообщ/сек) — обязательно webhook + очередь: webhook просто кладёт update в Redis/SQS, воркеры разбирают. Иначе на пике сервер ляжет.
// app/api/webhook/route.ts
import { NextResponse } from 'next/server';
import { Bot, webhookCallback } from 'grammy';
const bot = new Bot(process.env.BOT_TOKEN!);
bot.command('start', (ctx) => ctx.reply('Привет с serverless!'));
bot.on('message:text', (ctx) => ctx.reply(`Эхо: ${ctx.message.text}`));
const handler = webhookCallback(bot, 'std/http');
export async function POST(req: Request) {
// 1. Проверка secret token
const secret = req.headers.get('x-telegram-bot-api-secret-token');
if (secret !== process.env.WEBHOOK_SECRET) {
return NextResponse.json({ ok: false }, { status: 401 });
}
// 2. Передаём в grammY
return handler(req);
}
// Один раз — регистрация webhook (запустить в скрипте при первом деплое):
// import { Bot } from 'grammy';
// const bot = new Bot(process.env.BOT_TOKEN!);
// await bot.api.setWebhook('https://yourdomain.vercel.app/api/webhook', {
// secret_token: process.env.WEBHOOK_SECRET,
// drop_pending_updates: true,
// });
// console.log('Webhook set');
// .env:
// BOT_TOKEN=12345:abc...
// WEBHOOK_SECRET=любая_длинная_случайная_строка_32_символа
// Деплой:
// 1. git push в Vercel
// 2. В дашборде Vercel добавить env-переменные
// 3. Запустить скрипт setWebhook один раз
// 4. Бот живёт без сервера 24/7, ты не платишь пока никто не пишетWebhook на Next.js — деплой на Vercel за 5 минут