Stripe Webhookの署名検証に失敗するとき、最初の30分でやること
Stripe Webhookの署名検証(signature verification)で失敗しているときに、最初の30分で「秘密鍵取り違え / body改変 / 到達性」を切り分けて止血するチェックリストです。
Stripe Webhookで 署名検証(signature verification)に失敗 しているとき、最初の30分でやるべきことは「本当に署名の不一致なのか」「エンドポイントやシークレットの取り違えなのか」「“署名以前”の前処理(bodyの改変など)が原因なのか」を切り分けて、止血することです。
この事故は、支払い・サブスク更新・権限付与など 売上に直結する処理が丸ごと止まる ことが多いので、最初の30分は“正しさの追求”より 確実に原因候補を潰す順番 を固定します。
0. まずやらないこと(事故を拡大しやすい)
- いきなりWebhook Secretをローテーションする(原因が見えなくなりやすい)
- いきなり「とりあえず検証を無効化」して通す(再発しやすい)
- いきなりイベント種別やエンドポイントを増やす(ノイズが増える)
最初の30分は「どの秘密鍵で」「どの環境の」「どの生bodyで」検証しているかを確定させます。
1. 症状を1行で固定する(3分)
まず、何が起きているかを固定します。
- どのイベントで失敗しているか(例:
checkout.session.completed) - どの環境か(本番/ステージング、Webhook endpointのURL)
- いつからか(デプロイや環境変数変更と突き合わせる)
- エラー文言(そのまま保存)
ここが曖昧だと、修正しても“違う場所”を見ていて再発します。
2. 「秘密鍵の取り違え」を最優先で潰す(7分)
署名検証失敗の最頻出はこれです。
- Webhook endpointが 別環境のSigning secret を参照している(本番↔ステージング)
- 古いsecret を参照している(設定変更後に反映されていない)
- endpointを複数持っていて、参照するsecretが混ざっている
最初の30分は、まず「このendpointでは、このSigning secretで検証している」を1つに固定します。
3. 「生のrequest bodyが改変されている」を潰す(10分)
署名検証は“受け取った生body”に依存します。次があると不一致になりやすいです。
- JSONを一度パースして、再度JSON文字列化 したものを検証している
- bodyを読み取る前に、フレームワークのmiddleware等で ボディが消費 されている
- 改行コードや文字コードの変換が入っている
止血としては、「署名検証に使っている入力が、Stripeが送った生bodyそのものか」を確認します。
3-1. まず“検証材料”を固定する(ログの取り方)
最初の30分は、中身を全部ログに出さず「一致/不一致が判断できる材料」を固定します。
Stripe-Signatureヘッダが届いているか(空/欠落なら署名以前の問題)- 署名検証に使っている Signing secret がどれか(環境変数名・設定画面のどれ、まで固定)
- 生bodyの バイト数(例:
rawBodyBytes=1234) - 生bodyの ハッシュ(例: SHA-256 を16進で1行。中身は出さない)
注意: Webhookペイロードには個人情報や決済情報が含まれることがあります。生bodyをそのまま永続ログに出す のは避け、最小限のメタ情報(長さ/ハッシュ/イベントIDなど)に絞ってください。
3-2. “よくある失敗パターン”と最短修正
パターンA: JSONをパースしてから検証している
署名検証は「Stripeが送った生の文字列/バイト列」に対して行います。以下の形になっている場合は失敗しやすいです。
// NG例(概念)
const parsed = await req.json()
const regenerated = JSON.stringify(parsed)
// regenerated を署名検証に渡す
対処はシンプルで、パースより前の生body(例: req.text() や raw buffer)を署名検証に渡します。
パターンB: bodyを先に読み取ってしまい、後で署名検証している
middleware やログ用処理などで body を先に消費すると、後段の検証が別物になります。署名検証は 受信ハンドラの最初 に寄せ、検証が終わってからパースします。
3-3. 実装側の“チェック観点”だけ固定する(フレームワーク依存を避ける)
実装は言語/フレームワークで異なりますが、事故の原因はだいたい同じです。次を満たしているかだけを確認してください。
- 署名検証に渡している入力が「受信した生body」になっている(再文字列化していない)
Stripe-Signatureをそのまま検証に渡している(別ヘッダ名/別キーを見ていない)- 検証処理の前に body を消費する処理がない(middleware / logger / JSON parser など)
この3点が揃えば、少なくとも「生body問題」からは抜けられます。あとは Signing secret取り違え と 到達性 を疑う順番に戻れます。
4. 「どのendpointに届いているか」を潰す(5分)
環境やURLの切り替えで、意図しないendpointに届いているケースがあります。
https://.../api/webhookなどURLの変更/リダイレクトが入っていないか- 認証やWAFで弾いていないか(リダイレクト/HTMLが返っている等)
- 受信ログが取れているか(届いているのに“違うアプリ”が処理していないか)
「届いていない」場合は、署名検証以前の問題なので、到達性の復旧を優先します。
5. 30分で出す“結論”の型(次の作業を固定)
最初の30分で、結論を次のどれかに落とします。
- Signing secretの取り違え: endpointとsecretの対応を固定し直す(本番/ステージング混入の解消)
- 生bodyの改変/消費: 署名検証に使う入力を“生body”に戻す(前処理を見直す)
- endpoint/到達性の問題: 正しいendpointに届く状態を戻してから署名検証を再確認
これが決まれば、次の1時間で“直す場所”を間違えません。
SiteOpsで初動を速くする(再発防止)
Webhook事故は、原因が複合しているほど「いつから」「どのサイトで」「どの変更が」起点かの確認が遅れます。次からは、異常と変更点を並べて見られる状態にしておくと初動が速くなります。
- まずは ダッシュボード で、Stripe系の異常と優先順位を固定します
- 相談したい場合は お問い合わせ から状況を送ってください(止血の順番を一緒に整理できます)
- 料金と導入の流れは 料金ページ にまとめています
関連
- /media/stripe-webhook-delivery-failure-first-30-minutes
- /media/stripe-checkout-cvr-drop-first-30-minutes
- /media/stripe-mrr-drop-first-30-minutes
参考にした一次情報
この記事を書いた人
川原
SiteOps編集チームの公開窓口として、検索、アクセス、収益データをもとにした運営判断の知見をまとめています。
関連記事
次にやること
複数サイトの検索、アクセス、収益データをまとめて見直すなら、SiteOpsのダッシュボードでサイト別に確認できます。
料金を見る相談したい / お問い合わせ