Webhook Security
Webhook hardening path
Start with native launch mode, then move to framework adapters when you already own the HTTP server.
Native launch mode
Let VibeGram own the HTTP server lifecycle.
Framework adapters
Mount a secure webhook handler in Express, Fastify, Hono, Koa, or native HTTP.
Body limits
Reject oversized payloads before JSON parsing reaches your handlers.
Setup
import express from 'express';
import { Bot, createExpressMiddleware } from 'vibegram';
const bot = new Bot(process.env.BOT_TOKEN!);
const app = express();
const webhook = createExpressMiddleware(bot, {
secretToken: process.env.WEBHOOK_SECRET,
healthPath: '/healthz',
});
app.post('/webhook', express.json({ limit: '1mb' }), webhook);
app.get('/healthz', webhook);
await bot.setWebhook('https://your-domain.com/webhook', {
secret_token: process.env.WEBHOOK_SECRET,
});
app.listen(3000);How It Works
- You register the webhook with Telegram and include
secret_token. - Telegram sends that value in
X-Telegram-Bot-Api-Secret-Token. - VibeGram validates the header before processing the update.
- Invalid or missing tokens receive
403 Forbidden. - Malformed update bodies receive
400 Bad Request.
Native Launch Mode
For standalone deployments, bot.launch({ webhook }) can create the HTTP server, register the webhook, and shut down gracefully:
await bot.launch({
webhook: {
url: process.env.WEBHOOK_URL!,
port: Number(process.env.PORT ?? 3000),
path: '/webhook',
secretToken: process.env.WEBHOOK_SECRET,
healthPath: '/healthz',
maxBodySizeBytes: 1_000_000,
},
});healthPath returns 200 OK without validating the Telegram secret token or processing an update body.
Framework Adapters
All webhook adapters support the same secretToken and healthPath shape:
| Adapter | Import | Notes |
|---|---|---|
| Express | createExpressMiddleware | Mount body parser only on the webhook route |
| Fastify | createFastifyPlugin | Use Fastify's bodyLimit for payload caps |
| Hono | createHonoHandler | Pair with runtime/platform body limits |
| Koa | createKoaMiddleware | Use koaBody({ jsonLimit: '1mb' }) |
| Native HTTP | createNativeHandler | Uses maxBodySizeBytes directly |
Body Limits
Telegram updates are small in normal use. Keep limits tight enough to protect your parser and infrastructure:
| Adapter | Where to set the limit |
|---|---|
Native bot.launch({ webhook }) / createNativeHandler() | maxBodySizeBytes, default 1 MB |
| Express | express.json({ limit: '1mb' }) |
| Fastify | Fastify({ bodyLimit: 1_000_000 }) |
| Hono | Platform/runtime request body limit |
| Koa | koaBody({ jsonLimit: '1mb' }) |
Do not mount a broad unlimited body parser before webhook secret validation.
Register and Remove Webhooks
await bot.setWebhook(`${process.env.WEBHOOK_URL}/webhook`, {
secret_token: process.env.WEBHOOK_SECRET,
max_connections: 100,
allowed_updates: ['message', 'callback_query'],
});
const info = await bot.getWebhookInfo();
console.log(info.pending_update_count);
await bot.deleteWebhook({ drop_pending_updates: true });Deployment Checklist
- Terminate TLS in front of the webhook endpoint.
- Set a random
secret_tokenand keep it in environment variables. - Accept
POSTonly on the webhook route. - Mount JSON parsing only on the webhook route with a size limit.
- Expose a lightweight health endpoint for platform probes.
- Do not log bot tokens, webhook secrets, or raw request headers.
Without Secret Token
app.post('/webhook', bot.webhookCallback());WARNING
Without a secret token, any client that knows your webhook URL can send fake updates. Always use a secret token in production.