Vibe Coder

Webhook 是什麼?接收、驗證、本機測試全攻略

Webhook 是對方主動通知你事件發生的機制,比你自己每隔一段時間去問更有效率。接收端要先驗證 HMAC 簽名確認來源真實,再處理資料,並盡快回傳 200,耗時的處理要放到背景執行。
Webhook 是什麼?接收、驗證、本機測試全攻略

Webhook 是什麼?接收、驗證、本機測試全攻略

付款結果、訂單狀態變更、LINE 訊息、GitHub push,這些事件無法主動查詢,只有在發生時才知道,要靠 Webhook 接收通知。每秒問一次有沒有新訊息很浪費資源,Webhook 的邏輯反過來,是有訊息時才通知你,即時且省資源。

如果你在做自動化工作流,例如 n8n、Zapier,Webhook 是最常見的觸發器,懂了才能設計完整的自動化流程。而任何人都可以對你的 Webhook URL 發送請求,不驗證簽名,就可能被偽造的事件欺騙,執行錯誤的操作。

你將學到什麼

Webhook vs Polling

對方主動推 vs 你主動問,兩種溝通方式的效率差在哪裡。

完整接收流程

建端點、對方後台設定、驗證簽名、立刻回傳 200 的四個步驟。

HMAC Signature 驗證

任何人都能偽造 Webhook 請求,簽名驗證是唯一的防線。

本機測試工具

用 ngrok、smee.io 把 localhost 暴露出去,實際測試流程。

Webhook vs API Polling:兩種溝通方式

方式誰主動即時性效率適用場景
API Polling你主動問取決於詢問頻率低,大量無效請求需要主動控制、對方不支援 Webhook
Webhook對方主動推幾乎即時高,只在有事件時觸發付款、訊息、GitHub 事件、訂單狀態
最好的類比Polling 就像你每五分鐘打電話問朋友到了嗎;Webhook 就像朋友到了自己打電話告訴你我到了。

Webhook 的完整接收流程

  1. 建立 Webhook 端點:在後端建立一個 POST 路由,例如 /webhook/stripe,這個 URL 就是你的 Webhook URL。
  2. 在對方後台設定:到 Stripe、GitHub、LINE 等服務的後台,填入你的 Webhook URL 和想接收的事件類型。
  3. 接收並驗證:對方有事件時會 POST 到你的 URL,要先驗證簽名確認是真的來自對方,再處理資料。
  4. 立刻回傳 200:接收到 Webhook 後要盡快回傳 200,告訴對方我收到了,耗時的處理放到背景執行。
回傳 200 OK(立刻)Stripe發送事件你的端點驗證 HMAC 簽名處理事件內容
驗證簽名在前,處理資料在後,回應一律要快。
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhook/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")

    # 步驟一:驗證簽名,下一節說明
    event = verify_stripe_signature(payload, sig_header)

    # 步驟二:根據事件類型處理
    if event["type"] == "payment_intent.succeeded":
        handle_payment_success(event["data"]["object"])

    return {"status": "ok"}  # 立刻回傳 200

驗證來源:HMAC Signature

不驗簽名的 Webhook 端點,任何人知道 URL 都能偽造事件。HMAC 簽名讓你能確認請求真的來自你設定的服務:你和對方共享一個 Webhook Secret,對方發送時用這個 Secret 對 Request Body 做 HMAC-SHA256 運算,把結果放在 Header 裡;你收到後用相同的 Secret 對 Body 做相同運算,比對結果,一致就是真的,不一致就是偽造的,拒絕並回傳 400。

import hmac, hashlib

def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    received = signature.replace("sha256=", "")
    # 用 compare_digest 防止 timing attack
    return hmac.compare_digest(expected, received)
不要用等號比對簽名用等號比對字串有 timing attack 風險,攻擊者可以透過回應時間猜出簽名。一定要用固定時間比對的函式,也就是 hmac.compare_digest。

常見 Webhook 場景

服務Signature Header事件範例
StripeStripe-Signature付款成功、退款、訂閱到期
GitHubX-Hub-Signature-256push、PR、issue 建立
LINE Messaging API無,改用 Channel Secret 驗證使用者傳訊息、加好友
ShopifyX-Shopify-Hmac-Sha256訂單建立、商品更新
n8n(觸發節點)可選,自訂自定義工作流觸發

本機測試 Webhook:ngrok 和 smee.io

本機的 localhost 外部無法存取,要測試 Webhook 需要把本機暴露到公開網路。

工具用法特點
ngrokngrok http 8000建立臨時公開 URL,轉發到本機
smee.io建立頻道,用 CLI 轉發GitHub 官方推薦,可重播請求
Cloudflare Tunnelcloudflared tunnel免費、穩定,支援自訂域名
測試 Webhook 的標準流程先啟動本機後端服務;再啟動 ngrok 取得臨時 URL;把這個 URL 填到 Stripe 或 GitHub 等服務的 Webhook 設定;最後觸發事件,例如發送 LINE 訊息或 push 到 GitHub,觀察本機終端機收到的請求。

延伸學習

把這篇文章分享給需要的人FacebookLINEThreadsX

常見問答

Webhook 和 API Polling 差在哪裡?
API Polling 是你主動定期詢問服務有沒有新資料,效率低而且大量請求都是無效的;Webhook 是對方在事件發生時主動推送給你,幾乎即時,也只在有事件時才觸發,效率高很多。
為什麼一定要驗證 Webhook 的簽名?
因為任何人只要知道你的 Webhook URL,都能發送偽造的請求。不驗證簽名,你的系統可能被騙去執行錯誤的操作,例如偽造一筆付款成功的通知。
驗證簽名的時候可以直接用等號比對字串嗎?
不建議。用等號比對字串有 timing attack 的風險,攻擊者能透過回應時間差異猜出簽名內容,一定要用固定時間比對的函式,例如 Python 的 hmac.compare_digest。
本機開發時要怎麼測試 Webhook?
本機的 localhost 外部無法存取,需要用 ngrok、smee.io 或 Cloudflare Tunnel 這類工具,把本機暫時暴露到一個公開網址,再把這個網址填到服務的 Webhook 設定裡。