GitHub 如何投递 Webhook

GitHub 从不重试失败的投递,3 天后记录就没了。本页说明这意味着什么,并给出签名格式和 Node 验证器。

投递行为

取自该平台的官方文档,最近核对于 2026-08-06。平台没有给出数字的地方,这张表也不填。

响应时限10 秒

检测你的处理程序能否及时应答

重试无。GitHub 不会自动重新传递投递失败的 Webhook
放弃不会停用。投递在 3 天内都可以手动重新传递,之后记录不再保留
出处https://docs.github.com/en/webhooks/using-webhooks/best-practices-for-using-webhooks

签名

用一个经测试套件证明的可运行验证器核对,不是照着文档读来的。最近核对于 2026-08-20。

算法HMAC-SHA256
待签名串raw body
编码hex
请求头x-hub-signature-256 = sha256=hex
时间容差无强制要求

验证签名

要用原始请求体,逐字节一致,并且在任何 JSON 解析之前。框架一旦重新序列化请求体,本页的每一种方案都会失效。

const crypto = require("node:crypto");

// x-hub-signature-256: "sha256=" + hex(HMAC-SHA256(secret, raw body))
const expected =
  "sha256=" +
  crypto.createHmac("sha256", webhookSecret).update(rawBody).digest("hex");
const received = Buffer.from(req.headers["x-hub-signature-256"] ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  received.length === wanted.length && crypto.timingSafeEqual(received, wanted);

容易踩的坑

你只有一次尝试。这张列表上的其他发送方都会自己重试;GitHub 不会,所以一个 30 秒的部署窗口就会丢事件,而且没有第二次机会。重新传递按钮(以及 Redelivery API)都是按单次投递手动操作的,只在 3 天内有效;之后连记录本身都没了。

延伸阅读

AnyHook 挡在接收 GitHub Webhook 的端点前面:约 1 秒应答,你的服务器宕机时按自己的计划重试,并且每个事件都可以重放。 只改一个 URL,代码不用动。

工作原理 →