Webhooks 詳解:簽名負載與重試機制

Backend2026-11-06TryQuickToolBox

你已經整合了支付閘道或 CI/CD 服務,現在需要接收即時事件。Webhook 是標準解決方案,但它們帶來兩大挑戰:驗證請求確實來自預期的發送方,以及在你的端點宕機或緩慢時處理失敗。本文解釋如何實現簽名的 Webhook 負載和穩健的重試邏輯,並提供實用的程式碼範例。

什麼是 Webhook?

Webhook 是一種使用者定義的 HTTP 回呼。每當事件發生時,提供者會向您指定的 URL 發送 HTTP POST 請求,而不是由您的應用程式輪詢 API 以獲取更新。這使得整合高效且近乎即時。

然而,Webhook 是基於推送的,這意味著您的端點必須公開可訪問、處理意外負載,並應對重複或惡意請求。這就是簽名和重試發揮作用的地方。

為什麼要簽名 Webhook 負載?

如果沒有驗證,任何知道您的 Webhook URL 的人都可以發送虛假事件。使用共享密鑰對負載進行簽名,可以讓您驗證發送方並確保負載未被篡改。

最常見的方法是使用 SHA-256 的 HMAC(基於雜湊的消息驗證碼)。提供者使用密鑰對原始請求主體計算簽名,並將其包含在標頭中(例如 X-Signature)。您的伺服器重新計算簽名並進行比較。

實現 HMAC 驗證

以下是使用 Express 在 Node.js 中的簡單範例:

const crypto = require('crypto');
const express = require('express');
const app = express();

// 使用原始主體進行簽名驗證
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  const payload = req.body; // Buffer

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
    return res.status(401).send('Invalid signature');
  }

  // 處理事件
  const event = JSON.parse(payload);
  console.log('Received event:', event.type);
  res.status(200).send('OK');
});

app.listen(3000, () => console.log('Webhook server running'));

關鍵點:

處理重試:讓 Webhook 可靠

網路會故障,伺服器會崩潰,部署會導致停機。可靠的 Webhook 系統必須重試失敗的傳遞。但簡單的重試可能導致重複處理和系統過載。

重試策略

常見的重試模式包括:

大多數提供者實現類似:在 24 小時內最多重試 5 次,採用指數退避。

冪等性:安全重試的關鍵

由於重試可能多次傳遞同一事件,您的端點必須是冪等的。在負載中包含唯一的事件 ID,並存儲已處理的 ID 以跳過重複。

使用簡單的記憶體集合範例(在生產環境中使用資料庫):

const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  // ... 簽名驗證 ...
  const event = JSON.parse(payload);
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed');
  }
  // 處理事件
  processedEvents.add(event.id);
  res.status(200).send('OK');
});

在生產環境中,使用帶有事件 ID 唯一約束的資料庫。

Webhook 消費者的最佳實踐

重試策略比較

策略 優點 缺點
固定間隔 易於實現 可能使正在恢復的系統過載
指數退避 在停機期間減少負載 如果暫時性故障會延遲傳遞
帶抖動的指數退避 防止同步重試 稍微複雜一些

常見問題

如何驗證 Webhook 簽名?

使用共享密鑰計算原始請求主體的 HMAC,並使用時間安全函數將其與簽名標頭進行比較。切勿使用簡單相等進行比較。

如果我的 Webhook 端點宕機了怎麼辦?

提供者應使用指數退避進行重試。確保您的端點快速返回 2xx 並非同步處理事件,以最小化停機影響。

我可以使用沒有簽名的 Webhook 嗎?

不建議。沒有簽名,您無法驗證發送方,使您的端點容易受到虛假事件的攻擊。當可用時始終使用簽名。

在測試 Webhook 負載時,您可能需要檢查 JSON 資料。使用 JSON Formatter 快速美化列印和驗證負載。