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

Backend2026-10-24TryQuickToolBox

您已經建立了一個 API,現在需要在事件發生時通知其他服務。您設置了一個 webhook 端點,但很快發現可靠地傳遞 HTTP 請求比看起來更困難。網路故障、惡意行為者和重複事件可能造成混亂。本指南說明如何實作帶有簽名載荷和重試的 webhooks,以便您建立穩健的整合。

什麼是 Webhooks?

Webhooks 是使用者定義的 HTTP 回呼。當您的應用程式中發生事件時,您向訂閱者提供的 URL 發送 HTTP POST 請求。這允許服務之間進行即時通訊,而無需輪詢。

例如,支付處理器可能在扣款成功時向您的應用程式發送 webhook。您的應用程式可以立即更新訂單狀態。

為什麼要簽名 Webhook 載荷?

Webhook 端點是公開可存取的,因此任何人都可以發送偽造請求。如果沒有驗證,您的應用程式可能會處理欺詐事件。簽名載荷確保請求來自您的服務且未被篡改。

簽名通常使用發送方和接收方共享的密鑰。發送方對載荷計算基於雜湊的訊息驗證碼(HMAC),並將其包含在標頭中。接收方重新計算 HMAC 並進行比較。

如何生成和驗證簽名

大多數實作使用 HMAC-SHA256。以下是一個 Python 的簡單範例:

import hmac
import hashlib
import json

def generate_signature(payload, secret):
    body = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    signature = hmac.new(secret.encode('utf-8'), body, hashlib.sha256).hexdigest()
    return signature

def verify_signature(payload, signature, secret):
    expected = generate_signature(payload, secret)
    return hmac.compare_digest(expected, signature)

發送 webhook 時,將簽名包含在類似 X-Signature 的標頭中。接收方提取原始主體,重新計算 HMAC 並進行比較。始終使用常數時間比較函數以防止時序攻擊。

實作重試以實現可靠傳遞

Webhooks 透過網際網路發送,因此失敗是不可避免的。您的伺服器可能宕機、網路可能緩慢,或者訂閱者可能返回錯誤。使用指數退避的重試至關重要。

重試策略

一種常見方法是使用遞增的延遲進行重試:1 分鐘、5 分鐘、30 分鐘、2 小時等。在達到最大嘗試次數(例如 10 次)或總時間窗口(例如 24 小時)後停止。

僅在特定條件下重試:網路錯誤、5xx 回應或 429(請求過多)。不要對 4xx 錯誤(429 除外)重試,因為它們表示客戶端錯誤,重試無法修復。

重試邏輯範例

以下是使用 requests 庫的 Python 簡化重試機制:

import requests
import time
from requests.exceptions import RequestException

def send_webhook(url, payload, secret, max_retries=5):
    headers = {'Content-Type': 'application/json'}
    signature = generate_signature(payload, secret)
    headers['X-Signature'] = signature
    
    for attempt in range(max_retries):
        try:
            response = requests.post(url, json=payload, headers=headers, timeout=10)
            if response.status_code < 500 and response.status_code != 429:
                return response.status_code
        except RequestException:
            pass
        
        # 帶有抖動的指數退避
        delay = (2 ** attempt) + (random.random() * 0.1)
        time.sleep(delay)
    
    return None  # 重試後失敗

在生產環境中,您會將失敗的嘗試存儲在佇列中並非同步處理。Celery 或雲端佇列(AWS SQS、Google Cloud Tasks)等工具可以提供幫助。

Webhook 安全最佳實踐

處理重複和冪等性

重試可能導致重複傳遞。在載荷中包含唯一事件 ID,並讓接收方追蹤已處理的 ID。如果某個事件 ID 之前已見過,則忽略它。

或者,將事件處理常式設計為冪等。例如,多次使用相同資料更新記錄應與執行一次具有相同的效果。

測試 Webhooks

使用 ngrok 等工具暴露您的本機伺服器並測試 webhook 傳遞。透過返回錯誤碼模擬失敗,並驗證重試是否按預期發生。

常見問題

什麼是簽名 webhook 載荷?

簽名載荷包含加密簽名(通常是 HMAC),允許接收方驗證請求來自受信任的來源且未被修改。

我應該重試失敗的 webhook 多少次?

這取決於您的可靠性需求。常見做法是在 24 小時內使用指數退避重試 5-10 次。始終允許訂閱者手動觸發重試。

我可以不使用簽名嗎?

可以,但有風險。沒有簽名,任何人都可以發送偽造事件。在生產環境中始終簽名 webhooks 以防止欺詐。

準備好測試您的 webhook 載荷了嗎?在發送之前,使用我們的 JSON Formatter 驗證和美化 JSON 載荷。