独立站Webhookの信頼性:再試行、冪等性、署名検証、監視

Webhookは「送れば終わり」ではなく、確認付きの配信プロトコルです。相手が3秒以内に2xxを返さなければ、再送する必要があります。信頼できるWebhookには四つの条件を同時に守る必要があります:HM...

Webhook 可靠性四要素:收得到、认得准、不重复、看得见Webhook reliability: receive, verify, dedupe, observe 反直觉:Webhook 不是”发了就行”——对方不回 2xx,你就必须重发Counter-intuitive: a webhook is not sent until a 2xx comes back 要素Element 做法How 量化标准Standard 签名验证Signature HMAC-SHA256 校验头HMAC-SHA256 header check 时间戳容差 5 分钟5-min timestamp skew 快速返回Fast ack 先回 2xx 再异步处理Ack 2xx, process async 3 秒内必须响应Respond within 3s 幂等去重Idempotent 事件 ID 落表去重Dedupe by event id 同一事件重试 N 次只处理 1 次Retry N times, process once 重试退避Retry backoff 指数退避 + 抖动Exponential + jitter 最多 5 至 8 次,约 24 小时5-8 retries over ~24h 核心原则:先验证签名,再快速回 2xx,事件入库后异步处理Verify signature, ack fast, then process async 不要在同步请求里做耗时业务,否则对方判定超时开始重发,造成重复Never do slow work inline or retries will duplicate work 监控红线:重试队列堆积超过 100 条或连续 30 分钟无成功投递,立即告警Alert: backlog > 100 or zero success for 30 min 超过重试上限的事件进死信队列,人工补单,不能静默丢弃Dead-letter failed events; never silently drop
Webhookの信頼性を支える四要素:署名検証、即時応答、冪等な重複排除、再試行バックオフとデッドレター監視

独立站Webhookの信頼性:再試行、冪等性、署名検証、監視

結論から言うと:Webhookは「送れば終わり」ではなく、確認付きの配信プロトコルです。相手が3秒以内に2xxを返さなければ、再送する必要があります。信頼できるWebhookには四つの条件を同時に守る必要があります:HMAC署名検証で偽造を防ぐ、まず素早く2xxを返してから非同期で処理する、イベントIDで冪等に重複排除する、指数バックオフ再試行とデッドレターキューを組み合わせる。最も多い事故は「届かなかった」ではなく、「届いたのに二回処理した」、あるいは「処理が遅くて再送が発生し、同じ注文が三件になった」です。

一、Webhookが本質的に信頼できない理由

Webhookは相手のシステムが能動的にイベントをあなたのコールバックURLにPOSTする仕組みです。HTTPの上に成り立っていますが、HTTP自体はコネクションレスで到達を保証しません。ネットワークが一瞬乱れる、サービスが再起動する、処理がタイムアウトする、それだけでこの配信は失われる可能性があります。さらに厄介なのは「相手が受け取ったかどうか分からない」ことです。送信したのに2xx応答が返らないとき、ネットワークで失われたのか、相手が処理したが応答だけ失われたのか、判別できません。

ここでの直感に反する判断はこうです。Webhookシステムはデフォルトで再試行するため、受信側は「重複配信される」前提で設計する必要があります。一度しか来ないと仮定してはいけません。StripeやShopifyのような成熟したプラットフォームの標準的な方法は、受信側が2xxを返さなければ指数バックオフで再送し、最大5〜8回、約24時間にわたって試行するというものです。

冪等性を実装していなければ、同じ決済イベントが何度も計上されます。リードのシステム連携についてはサイトのリードから CRM へをご覧ください。そこでも同じく重複イベントに対処する必要があります。

四つの要素のどれか一つが欠けると、信頼性は一段下がります。次の表で各要素の「やること」と「漏れたときの結果」を整理します。

要素 役割 標準的な実装 漏れたときの結果
署名検証 リクエストが本当に相手から来たか確認する HMAC-SHA256でヘッダーを検証 偽造イベントで不正な注文が入る
即時応答 相手のタイムアウト再送を防ぐ 先に2xxを返し、その後で非同期処理 処理が遅くて重複配信が起きる
冪等な重複排除 重複イベントを一度だけ処理する イベントIDをテーブルに記録して排除 同じ注文が何度も計上される
再試行バックオフ 一時的な失敗でも最終的に届く 指数バックオフにジッターを加える 偶発的な障害で注文がそのまま失われる
デッドレター監視 失敗イベントを黙って捨てない 上限超過はデッドレターキューに入れてアラート 長期的に注文が失われても誰も気づかない

二、署名検証:このリクエストが他人の偽造でないことを確認する

Webhook のコールバック URL は誰でもアクセスできる公開 URL で、誰でもそこに POST できます。署名検証をしないと、攻撃者が「決済成功」の偽イベントを送りつけるだけで、管理画面側が本当に権限を付与したり注文を通してしまう恐れがあります。これは理論上のリスクではなく、実際に攻撃されたことのある攻撃面です。

標準的な方法は HMAC-SHA256 です。相手が取り決めた秘密鍵でリクエストボディに署名し、リクエストヘッダー(例: X-Signature)に付けます。受け取った側は同じ鍵で元のリクエストボディを再計算し、両方を照合して一致したときだけ処理します。

押さえるべき点は2つあります。1つ目は、通常の文字列比較(==)ではなく定数時間比較関数を使い、タイミング攻撃を防ぐこと。2つ目は、リクエストヘッダーのタイムスタンプを検証し、5分を超えるずれは即座に拒否して、古いリクエストを録音して再利用するリプレイ攻撃を防ぐことです。鍵が漏れたらすぐにローテーションできるようにしておきます。

検証項目 やり方 不合格時の対応
署名が一致するか HMAC-SHA256 で再計算して照合 そのまま 401 で拒否
タイムスタンプが新しいか サーバー時刻との差が5分以内 拒否してリプレイを防ぐ
鍵が期限切れか 新旧の鍵を並行してローテーション 古い鍵で再試行
リクエストボディが改ざんされていないか 元のバイト列で署名を計算 先に JSON デコードしてから署名しない

三、即時応答と非同期処理: リクエスト内で重い処理をしない

ここが最もつまずきやすいポイントです。Webhook を受け取ったあと、同期リクエストの中で DB 検索、メール送信、外部 API 呼び出し、ログ書き込みと重い処理を一通りこなして5秒かかってしまう人が多いです。相手は3秒待っても 2xx が返らないとタイムアウトと判断して再送を始めます。こちらはまだ1件目を処理中なのに、2件目がまた届くという状態です。

正しいのは3段階のパターンです。リクエストを受け取ったらまず署名を検証し(ミリ秒単位)、検証が通ったらすぐにイベントをそのまま DB に保存して 2xx を返し、そのあとバックグラウンドの非同期タスクでゆっくり処理します。こうすれば相手は3秒以内に必ず確認を受け取れ、再送は起きません。重い処理が遅くても、ユーザーにはすでに応答が返っているので問題ありません。処理に失敗したイベントはキューに残し、非同期タスクが自分でリトライします。

シナリオ例:決済成功イベントが届きます。受信側がやることは3つだけです。署名を検証し、イベント ID と元の payload を events テーブルに書き込み、すぐに 200 を返します。実際の「在庫追加、メール送信、ERP 通知」はバックグラウンドの worker が events テーブルから未処理イベントを取り出してゆっくり行います。メール送信に10秒かかっても、相手の再送には影響しません。フォーム送信の信頼性の考え方については、次と照らし合わせてください。問い合わせフォームの信頼性。

フェーズ アクション 所要時間の目標 失敗時の対応
同期受信 署名検証 + DB 保存 + 2xx 応答 300 ミリ秒未満 署名検証に失敗したら即拒否
非同期処理 DB 検索、通知送信、下流呼び出し 秒単位から分単位まで可 失敗したらキューに残してリトライ
リトライ バックオフ間隔で再実行 指数バックオフ 上限を超えたらデッドレターキューへ
手動での再処理 デッドレターキューを確認して手動実行 毎日の点検 再処理の理由を記録

四、冪等性とリトライ: 同じイベントが10回来ても1回しか処理しない

冪等とは、同じ操作を1回実行しても10回実行しても結果がまったく同じになることです。Webhook は本質的に冪等でなければなりません。相手の再送を止められないからです。実装は簡単で、相手が各イベントにグローバルに一意なイベント ID を付けるので、処理前にこの events テーブルを確認し、その ID を見たことがあれば成功を返して処理をスキップします。

再試行戦略は指数バックオフとジッターを組み合わせます。1回目の失敗は1分待ち、2回目は2分、3回目は4分と倍にしていき、最大5〜8回、合計で約24時間です。ランダムなジッターを加えるのは、失敗したイベントが同じ秒に集中して再試行され、サービスをもう一度圧迫するのを防ぐためです(これを再試行ストームと呼びます)。再試行の上限を超えたイベントは捨てずにデッドレターキューに入れ、人の対応を待ちます。

再試行回数 バックオフ間隔(約) 累計時間 説明
1回目 1分 1分 一時的なネットワークの揺らぎ
2回目 2分 3分 短時間のサービス再起動
3回目 4分 7 分 下流が一時的に利用不可
4回目 8分 15分 比較的長い障害
5〜8回目 15〜30分 約24時間 それでも失敗ならデッドレターキューへ

五、監視とデッドレター:注文の取りこぼしを黙って起こさない

Webhook で最も厄介な障害は「静かな取りこぼし」です。相手は送信に成功したと思っているのに、こちらには記録が1件もなく、数週間後に顧客から「支払ったのになぜ発送されないのか」と苦情が来て、イベントがそもそも届いていなかったと気づきます。だから監視では3つを必ず見ます。配信成功率、再試行キューの滞留、デッドレターキューの件数です。

しきい値は具体的な数字で設定します。再試行キューの滞留が100件を超えたら、または30分間連続で成功した配信が1件もなければ、普段使うチャネルにすぐアラートを送ります。デッドレターキューは毎日1回点検し、滞留があれば手動で再処理します。配信成功率が99%を下回ったら調査が必要です。健全な状態の Webhook 成功率は99.9%に近いはずで、99%を下回るなら継続的な問題があります。汎用的な監視アラートの組み方は次を参照してください。サイト監視とアラート。

監視指標 健全性 アラートしきい値 アクション
配信成功率 99.9%以上 1時間連続で99%未満 受信側のログを確認
再試行キューの滞留 ほぼ0 100件超 下流が詰まっていないか確認
デッドレターキュー 0 滞留が1件でも発生 手動で再処理し原因を特定
成功配信が連続でなし イベントは継続的にあり 30分間で0件 コールバック URL がブロックされていないか確認
署名検証の失敗回数 ほぼ0 10倍に急増 誰かが API をスキャンしている可能性

FAQ よくある質問

Webhook で時々重複した注文イベントを受け取るのはなぜですか?
2xx をすぐに返さないと、相手が自動で再送するからです。成熟したプラットフォームは既定で最大5〜8回、約24時間にわたって再送します。解決策は相手に再送をやめさせることではなく、こちら側でイベント ID による冪等な重複排除を行うことです。イベントを受け取ったらまずこのテーブルを確認し、処理済みなら成功を返して業務処理をスキップします。重複配信は Webhook では当たり前なので、重複する前提で設計しなければなりません。
Webhook を受け取ったらすぐ処理すべきですか、それとも先に返すべきですか?
まず即レスポンス。署名検証が通ったらすぐにイベントをDBに保存し、3秒以内に2xxを返す。実際の業務処理はバックグラウンドで非同期に行う。同期リクエストの中でメール送信やDB検索、下流API呼び出しに5秒かけていると、相手はタイムアウトと判定して再送を始め、結果的に重複が発生する。非同期ワーカーでゆっくり処理すればよく、遅くても問題ない。
HTTPS経由なら署名検証は不要では?
不要ではない。HTTPSが守るのは通信路上の盗聴防止だけであり、コールバックURLは誰でもアクセスできる公開アドレスなので、誰でも直接POSTを送りつけられる。署名検証をしなければ、攻撃者が「決済成功」の偽イベントを自作し、商品の発送や権限の付与を不正に実行させることができる。HMAC-SHA256でリクエストヘッダーの署名を検証し、さらに5分のタイムスタンプ許容幅を設けてリプレイ攻撃を防ぐこと。
リトライ間隔はどう決める?なぜ毎分固定のリトライではだめなのか?
指数バックオフとランダムジッターを組み合わせる。1分、2分、4分、8分と間隔を伸ばし、最大5〜8回、約24時間にわたってリトライする。毎分固定のリトライが問題なのは、下流が障害を起こすと失敗イベントが同じ秒に集中して再送され、復旧したばかりのサービスを再び圧壊させるからだ。これがリトライストームである。ランダムジッターでこの集中を分散させる。
何度リトライしても失敗するイベントはどう扱う?
黙って破棄してはいけない。デッドレターキューに入れる。リトライ上限を超えたイベントは専用テーブルに保存し、毎日1回点検して、担当者が確認したうえで手動で再処理を実行し、失敗理由も記録する。黙って破棄するのが最も危険なやり方だ。数週間後に顧客から「商品が届かない」と苦情が来ても、そのイベントが届いていたこと自体をログから確認できなくなる。
Webhookが黙って配信を落としていないか、どうやって気づく?
3つの数値を監視する。配信成功率が99%を下回ったら調査、リトライキューが100件を超えたらアラート、30分以上成功配信が1件もなければ即アラート。正常時は配信成功率が99.9%前後になるはずだ。さらに毎日デッドレターキューが0かどうかを確認する。これらのアラートを整備しておけば、黙った配信漏れは数週間後に顧客に指摘される前に、その日のうちに発見できる。

最後に

Webhookの信頼性の核心は、「一方通行のプッシュではなく、確認がなければ再送する双方向プロトコル」だという認識にある。受信側を設計するときは、「重複配信される」「偽造される」「失われる」を例外ではなくデフォルトの前提としなければならない。4つの要素のうち、冪等性による重複排除と迅速な確認応答は最も見落とされやすく、トラブルが最も多い。まずこの2つを正しく実装すれば、事故率は大きく下がる。

導入は3ステップ。第一に、受け側は署名検証・DB保存・3秒以内に2xxを返すことだけを行い、業務処理はすべて非同期にする。第二に、イベントIDで重複排除テーブルを作り、リトライは指数バックオフとジッターを組み合わせる。第三に、デッドレターキューを毎日点検し、配信成功率と滞留量をアラートに接続する。この3ステップを終えれば、コールバック経路は「たまに取りこぼして運任せ」から「取りこぼしても当日中に気づき、取り戻せる」状態になる。

覚えておきたい一言。Webhookの信頼性は、相手が安定して送ってくれることではなく、こちらが愚直に受け止めることにかかっている。署名を検証し、素早く返し、重複を排除する。二重処理になってもいいから、一件も取りこぼすな。

环形山
环形山 クレーター専門家

公式アカウント · 環形山 Crater

環形山(Crater)は、WordPress独立サイト開発+Google SEO+GEO全域成長に特化した海外展開支援会社で、スマートハードウェア、産業機器、SaaS、新エネルギーなどの分野を手がけています。

プロダクト能力方法論ホワイトペーパー業界レポート
965件のシェア
78件のナレッジベース