Skip to content

Webhook Security

Webhook hardening path

Start with the native launch mode, then move to framework adapters when you already own the HTTP server.

Native launch mode

Use `bot.launch({ webhook })` when VibeGram should own the HTTP server lifecycle.

Adapter health checks

Mount the same adapter on webhook and health routes where your framework needs both.

Framework adapters

Express, Fastify, Hono, Koa, and native HTTP share the same security shape.

When using webhooks, VibeGram supports Telegram's secret_token verification to prevent unauthorized requests.

Setup

typescript
import express from 'express';
import { Bot } from 'vibegram';

const bot = new Bot('YOUR_BOT_TOKEN');
const WEBHOOK_SECRET = 'my-secure-random-secret';

const app = express();
app.use(express.json());

// The callback validates X-Telegram-Bot-Api-Secret-Token header
app.post('/webhook', bot.webhookCallback(WEBHOOK_SECRET));

app.listen(3000);

How It Works

  1. When setting the webhook, include secret_token in the API call
  2. Telegram includes this token in the X-Telegram-Bot-Api-Secret-Token header
  3. webhookCallback() verifies the header matches your secret
  4. Requests with invalid or missing tokens receive 403 Forbidden

Native Launch Mode

For a standalone deployment, bot.launch({ webhook }) can own the native HTTP server and register the webhook for you:

typescript
await bot.launch({
    webhook: {
        url: process.env.WEBHOOK_URL!,
        port: Number(process.env.PORT ?? 3000),
        path: '/webhook',
        secretToken: process.env.WEBHOOK_SECRET,
        healthPath: '/healthz',
    },
});

healthPath returns 200 OK for uptime checks without validating the Telegram secret token or processing an update body.

Adapter Health Checks

When you use framework adapters, pass the same healthPath option and mount the adapter on both routes where the framework needs it:

typescript
import { createExpressMiddleware } from 'vibegram';

const webhook = createExpressMiddleware(bot, {
    secretToken: WEBHOOK_SECRET,
    healthPath: '/healthz',
});

app.post('/webhook', webhook);
app.get('/healthz', webhook);

Deployment Checklist

  1. Always terminate TLS in front of your webhook endpoint.
  2. Set a random secret_token when calling setWebhook.
  3. Restrict webhook routes to POST only.
  4. Use JSON body parsing only on the webhook route.
  5. Keep your bot token and webhook secret outside source control.
  6. Expose a lightweight health endpoint for load balancers and platform probes.

Adapter Hardening Notes

  • Native HTTP adapter rejects non-JSON requests with 415 Unsupported Media Type.
  • Native HTTP adapter rejects oversized bodies with 413 Payload Too Large.
  • healthPath responds with 200 OK before secret validation or update parsing.
  • All adapters return 500 Internal Server Error when update processing fails.
  • Invalid or malformed update payloads return 400 Bad Request.

Register the Webhook

typescript
await bot.callApi('setWebhook', {
    url: 'https://your-domain.com/webhook',
    secret_token: WEBHOOK_SECRET,
    allowed_updates: ['message', 'callback_query'],
});

Without Secret Token

If you don't need secret token validation:

typescript
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.

TIP

For native Node.js deployments, set maxBodySizeBytes explicitly if your platform requires a tighter body limit than the default 1 MB.

Released under the ISC License.