Telegram Bot Webhook 设置教程:用 setWebhook 配置 Webhook URL 和 secret_token
Telegram Bot Webhook 是 Telegram(电报)机器人接收消息的推送方式:一有新的更新,Telegram 就主动向你指定的 HTTPS 地址发送 POST 请求,你的程序不必再反复调用 getUpdates。设置方法是调用 Telegram Bot API 的 setWebhook,传入公网可访问的 HTTPS url,并建议同时设置 secret_token。本文介绍 Webhook URL 的要求、setWebhook 参数、请求头校验代码、如何用 getWebhookInfo 排查问题,以及切回轮询和常见报错的处理方法。
Webhook 和 getUpdates 有什么区别
| 对比项 | getUpdates(长轮询) | Webhook |
|---|---|---|
| 工作方式 | 你的程序主动拉取更新 | Telegram 把更新推送到你的 URL |
| 网络要求 | 能访问 api.telegram.org 即可,本地电脑也能运行 | 需要公网可访问的 HTTPS 地址 |
| 适合场景 | 开发调试、小型机器人 | 正式部署、Serverless、更新量大的机器人 |
两种方式互斥:设置了 Webhook,getUpdates 就会返回 409 Conflict。无论用哪种方式,尚未取走的更新最多在服务器上保存 24 小时。Webhook 收到的数据与 getUpdates 返回的一样,都是 JSON 格式的 Update 对象,只是每个请求只携带一个更新。还没有机器人的话,请先阅读 创建 Telegram 机器人教程。
Telegram Bot Webhook URL 的要求
- 必须是 HTTPS,支持 TLS 1.2 及以上版本,不能使用纯 HTTP。
- 端口只能是 443、80、88 或 8443。使用非 443 端口时把端口写进 URL,例如
https://bot.example.com:8443/tg-webhook。 - 证书有效:证书的 CN 或 SAN 必须与 URL 中的域名一致,并提供完整的中间证书链。使用自签名证书时,需要通过
certificate参数上传公钥证书(PEM 格式)。 - 使用 IPv4,Webhook 目前不支持 IPv6。
- 不支持重定向,URL 必须直接返回响应。
- 如果服务器配置了防火墙白名单,需要放行 Telegram 的推送网段
149.154.160.0/20和91.108.4.0/22(官方提示这个范围将来可能变化)。
本地开发时,localhost 无法被 Telegram 访问。要么继续用 getUpdates 调试,要么借助隧道(内网穿透)工具把本地端口映射为公网 HTTPS 地址。
调用 Telegram Bot API setWebhook 设置 Webhook URL
setWebhook 的常用参数:
| 参数 | 说明 |
|---|---|
url | 必填。接收更新的 HTTPS 地址;传空字符串表示删除 Webhook。 |
secret_token | 1–256 个字符,只能包含 A-Z、a-z、0-9、_ 和 -。设置后每个推送请求都会带上请求头 X-Telegram-Bot-Api-Secret-Token。 |
allowed_updates | 只接收指定类型的更新,如 ["message", "callback_query"]。不传则沿用上一次的设置;传空列表表示接收除 chat_member、message_reaction、message_reaction_count 之外的所有类型。 |
drop_pending_updates | 传 true 丢弃所有尚未推送的积压更新。 |
max_connections | 同时推送的最大 HTTPS 连接数,1–100,默认 40。服务器性能有限就调小,需要更高吞吐就调大。 |
certificate | 自签名证书的公钥文件,必须以文件形式上传(multipart/form-data)。 |
ip_address | 推送时使用的固定 IP 地址,不再通过 DNS 解析域名。 |
使用受信任 CA 签发的证书(例如 Let's Encrypt)时,一条 curl 命令即可完成设置:
curl -X POST "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/setWebhook" \
-H "Content-Type: application/json" \
-d '{
"url": "https://bot.example.com/tg-webhook",
"secret_token": "Kq8_x2-ReplaceWithRandomString",
"allowed_updates": ["message", "callback_query"],
"drop_pending_updates": true,
"max_connections": 40
}'
设置成功时返回:
{"ok": true, "result": true, "description": "Webhook was set"}
使用自签名证书时,需要通过 multipart/form-data 上传公钥文件:
curl -F "url=https://bot.example.com:8443/tg-webhook" \
-F "certificate=@public.pem" \
-F "secret_token=Kq8_x2-ReplaceWithRandomString" \
"https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/setWebhook"
secret_token 可以用 openssl rand -hex 32 生成(64 个十六进制字符,符合字符集要求)。也可以在 Bot API Tester 的 setWebhook 中填写参数后直接执行。
用 getWebhookInfo 检查 Webhook 状态
curl "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/getWebhookInfo"
{
"ok": true,
"result": {
"url": "https://bot.example.com/tg-webhook",
"has_custom_certificate": false,
"pending_update_count": 17,
"last_error_date": 1793266200,
"last_error_message": "Wrong response from the webhook: 500 Internal Server Error",
"max_connections": 40,
"allowed_updates": ["message", "callback_query"]
}
}
url为空:当前没有设置 Webhook。has_custom_certificate:是否上传了自定义(自签名)证书。pending_update_count:等待推送的更新数量,持续增长说明推送一直失败。last_error_date/last_error_message:最近一次推送失败的时间(Unix 时间戳)和原因,是排查问题的第一手线索。
在浏览器中也可以用 getWebhookInfo 随时查看这些字段。
在代码中校验 secret_token 请求头
Webhook URL 是公开的,任何人都可以向它发送伪造的“更新”。设置了 secret_token 后,Telegram 的每个请求都会带上 X-Telegram-Bot-Api-Secret-Token 请求头,处理程序应该先比对它,不一致就直接返回 403。Python(Flask)示例:
import hmac
import os
import requests
from flask import Flask, abort, request
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET = os.environ["TELEGRAM_WEBHOOK_SECRET"]
API = f"https://api.telegram.org/bot{TOKEN}"
app = Flask(__name__)
@app.post("/tg-webhook")
def webhook():
got = request.headers.get("X-Telegram-Bot-Api-Secret-Token", "")
if not hmac.compare_digest(got.encode(), SECRET.encode()):
abort(403) # 不是来自 Telegram 的请求
update = request.get_json(silent=True) or {}
msg = update.get("message")
if msg and "text" in msg:
requests.post(f"{API}/sendMessage", json={
"chat_id": msg["chat"]["id"],
"text": "收到:" + msg["text"],
}, timeout=10)
return "", 200
Node.js(Express)示例。这里演示另一种写法:直接在 Webhook 的响应体中返回要调用的方法(method 字段),可以省掉一次 API 请求,但无法得知这次调用是否成功:
import express from "express";
const SECRET = process.env.TELEGRAM_WEBHOOK_SECRET;
const app = express();
app.use(express.json());
app.post("/tg-webhook", (req, res) => {
if (req.get("X-Telegram-Bot-Api-Secret-Token") !== SECRET) {
return res.sendStatus(403);
}
const msg = req.body.message;
if (msg?.text) {
return res.json({ method: "sendMessage", chat_id: msg.chat.id, text: "收到:" + msg.text });
}
res.sendStatus(200);
});
// 由 Nginx 等反向代理在 443 端口处理 HTTPS,再转发到本机 3000 端口
app.listen(3000);
尽快返回 2xx,避免重复推送
Telegram 只把 2xx 状态码视为推送成功。返回其他状态码或请求超时时,它会重试同一个更新,多次失败后才放弃,期间 pending_update_count 会不断增加。因此:
- 收到请求后尽快返回
200,耗时任务(调用 AI 模型、生成文件等)放到后台队列中处理; - 用
update_id去重,同一个更新可能被推送不止一次; - 处理逻辑出错时也尽量返回 2xx 并记录日志,否则一条“有问题”的更新会被反复重试;
- 用
max_connections控制同时推送的请求数量,避免压垮服务器。
用 deleteWebhook 切回 getUpdates
curl "https://api.telegram.org/bot123456789:AAEexampleTokenReplaceWithYours/deleteWebhook?drop_pending_updates=true"
加上 drop_pending_updates=true 会同时清空积压的更新,想保留它们就去掉这个参数。用空字符串作为 url 调用 setWebhook 也能删除 Webhook。删除之后即可用 getUpdates 轮询,例如按 Telegram chat id 获取教程查看聊天 ID。
注意:在 Bot API Tester 中启动 getUpdates 长轮询时,工具会先自动调用 deleteWebhook(保留积压的更新)。如果你的正式机器人依赖 Webhook,测试结束后记得重新调用 setWebhook。
常见错误与排查
| 现象 | 原因 | 解决方法 |
|---|---|---|
setWebhook 返回 400,描述中包含 An HTTPS URL must be provided for webhook | URL 不是 HTTPS,例如填了 http://localhost:5678/... | 换成公网 HTTPS 地址;本地开发改用 getUpdates 或隧道 |
setWebhook 返回与端口有关的 400 错误 | 端口不是 443、80、88、8443 | 改用支持的端口,或通过反向代理转发 |
getUpdates 返回 409 Conflict | 已经设置了 Webhook,二者不能同时使用 | 调用 deleteWebhook |
last_error_message 中出现 SSL 或证书相关错误 | 证书链不完整、域名与证书不匹配、自签名证书没有上传 | 补全中间证书;自签名证书用 certificate 参数上传 |
Wrong response from the webhook: ...,pending_update_count 持续增加 | 处理程序返回了非 2xx:404(路径不对)、403(secret_token 不一致)、500(代码异常)等 | 核对路径和 secret_token;修复异常并尽快返回 200 |
| n8n 运行在 localhost,Telegram 触发器无法启用 | n8n 的 Telegram 触发器依赖 Webhook,而 Telegram 无法访问本地的 HTTP 地址 | 先为 n8n 配置公网 HTTPS 地址(域名或隧道),再启用触发器 |
还要记住:一个机器人同一时间只能有一个接收更新的地方。Webhook 一旦被 n8n 等工具注册,你自己的轮询脚本就会遇到 409;反过来,你调用 deleteWebhook 也会让这些工具收不到消息。如果同一个场景需要多个程序各自接收消息,最好为它们分别创建机器人,并各自保管 Token(参见 Telegram Bot Token 获取与管理教程)。
常见问题
Telegram Webhook 支持哪些端口?
只支持 443、80、88 和 8443 四个端口,并且必须使用 HTTPS。使用非 443 端口时要把端口写进 URL,例如 https://bot.example.com:8443/tg-webhook。
Webhook 的 secret_token 有什么用?
设置后,Telegram 的每个推送请求都会在请求头 X-Telegram-Bot-Api-Secret-Token 中带上这个值,服务器比对一致才处理,可以防止伪造的请求。长度 1–256 个字符,只能使用 A-Z、a-z、0-9、_ 和 -。
如何删除 Telegram 机器人的 Webhook?
调用 deleteWebhook(加上 drop_pending_updates=true 可同时丢弃积压的更新),或者用空字符串作为 url 调用 setWebhook。删除后就可以重新使用 getUpdates。
Webhook 设置成功了,为什么收不到消息?
先调用 getWebhookInfo,查看 last_error_message 和 pending_update_count。常见原因有证书链不完整、URL 发生重定向(不支持重定向),以及服务器返回了非 2xx 状态码。
本地 localhost 能直接使用 Webhook 吗?
不能。Telegram 必须能从公网通过 HTTPS 访问你的地址。本地开发可以改用 getUpdates 长轮询,或者用隧道工具把本地端口映射成公网 HTTPS 地址。
Webhook 和 getUpdates 可以同时使用吗?
不可以,二者互斥。设置 Webhook 后再调用 getUpdates 会返回 409 Conflict,要切回轮询必须先调用 deleteWebhook。
相关教程
Telegram 机器人教程:从零创建 Telegram 机器人并发送第一条消息
Telegram 机器人教程:在 @BotFather 中用 /newbot 创建机器人并获取 Token,用 getMe 验证,再通过 getUpdates 找到 chat_id,用 curl 和 Python 发出第一条消息,附常见问题排查。
Telegram Bot API 发送消息教程:sendMessage 参数、格式、按钮与错误处理
Telegram Bot API 发送消息中文教程:sendMessage 参数说明,curl、Python、JavaScript 示例,HTML 与 MarkdownV2 格式、内联按钮、群组和频道发送,以及 400、403、429 错误处理。
Telegram Bot Token 怎么获取:获取、查看、验证和撤销机器人 Token
Telegram Bot Token 怎么获取?用 @BotFather 的 /newbot 获取新 Token,在 /mybots 中查看已有 Token,用 getMe 检查是否有效,泄露后及时撤销,并配合 chat_id 接入 n8n 等工具。
Telegram chat id 获取教程:查看用户、群组、频道和话题的聊天 ID
Telegram chat id 获取教程:用 getUpdates 查看私聊、群组、超级群组、频道和论坛话题的聊天 ID,讲清负数和 -100 前缀的含义,并排查 result 为空、409 Conflict 等常见问题,附 Python 示例。