そのメールが届いたのは日曜日の朝でした。ヨーロッパのある非営利財団の業務責任者から、下記をご確認のうえ対応をお願いしますという一文だけを添えて転送されてきたものです。その下には Stripe からの自動通知がありました。数日前から寄付サイトへ Webhook イベントを配信できず、19 回の試行がすべて失敗しており、このままだと 1 週間以内にその endpoint への通知を停止する、という内容です。
WordPress で単発と毎月の寄付を受け付けている団体にとって、これは小さな警告ではありません。Webhook は、支払いが本当に完了したことをサイトが知るための仕組みです。これが止まると、寄付は保留中のまま、寄付者には領収書が届かず、毎月の更新も記録されません。その間もお金は Stripe に入金され続けているのに、です。
この記事では、障害をどう追跡したか、最初に見つけた問題が本物だったのに原因ではなかった理由、そしてセキュリティを一切無効にせずに解決した小さな変更について紹介します。
Stripe Webhook の役割と、失敗が問題になる理由
寄付者が支払うと、ブラウザは Stripe へ移動して戻ってきます。サイトはブラウザからの結果を信用できないため、Stripe はサーバーから直接サイトを呼び出し、payment_intent.succeeded のようなイベントを送ります。サイトは 200〜299 の HTTP ステータスで応答する必要があり、それ以外は失敗とみなされます。Stripe は間隔を広げながら最大 3 日間再試行し、その後そのイベントを諦めます。
今回の寄付プラグインは GiveWP で、?give-listener=stripe で終わるアドレスでイベントを受け取ります。サイトは多言語対応のため、/de/ と /fr/ の 2 つの endpoint が有効になっていました。Stripe のメールには 1 つしか書かれていませんでしたが、実際には両方とも失敗していました。

最初の発見:本当の問題だが、原因ではない
WordPress の管理画面にはすぐに有効化エラーが表示されました。Recurring Donations アドオンが、インストール済みより新しい GiveWP を必要としたために停止していたのです。アドオンだけが更新され、本体はそのままだったため、WordPress が静かに無効化していました。さらにライセンスも更新されない設定で、アップデートも受けられなくなっていました。
完璧な説明に見えましたし、いずれにせよ修正が必要でした。完全なバックアップを取り、GiveWP を更新してアドオンを再有効化したところ、公開ページがたちまち致命的エラーで停止しました。スタックトレースは、すでに削除された古い寄付フォームのショートコードを含むポップアップを指していました。アドオンを一旦無効化してサイトを復旧させ、ポップアップを修正してから再有効化し、トップページ、寄付ページ、すべての言語版を確認しました。
サイトは正常に戻り、毎月の寄付オプションも復活しました。しかし Webhook は失敗したままでした。
推測ではなく、実際のエラーを読む
推測をやめる最短の方法は、Stripe ダッシュボードの Developers → Webhooks で失敗した配信を 1 件開き、ステータスコードとレスポンス本文の 2 つを確認することです。ステータスは 403 Forbidden、本文は WordPress のエラーではなく、challenges.cloudflare.com からスクリプトを読み込む Just a moment… というタイトルの HTML ページでした。

このページは Cloudflare のブラウザチャレンジです。リクエストを通す前に、ブラウザに人間であることの証明を求めます。Chrome を使う人なら 1 秒で通過できますが、Stripe のサーバーはブラウザではないため解けず、すべての Webhook がエッジで止められていました。リクエストは一度も WordPress に届いておらず、それがサイトのログに何も残らなかった理由です。
Stripe をブロックしている Cloudflare の機能を特定する
Cloudflare にはトラフィックにチャレンジをかける機能が複数あり、どれが原因かによって対処が変わります。Security → Analytics → Events でクエリ文字列 give-listener で絞り込むと、該当するすべてのリクエストでアクションは Managed Challenge、サービスは Security level と表示されていました。

同じ一覧からは別のことも分かりました。スイス、フランス、ドイツの一般の訪問者も、普通のページでチャレンジを受けていたのです。ゾーンのセキュリティレベルが非常に高く設定されており、おそらく以前の攻撃への対策だったのでしょうが、決済サービスまで止めていることに誰も気づいていませんでした。
解決策:サイトを弱くするのではなく、絞り込んだ Skip ルール
セキュリティレベルを下げれば解決はしますが、1 つの URL のためにサイト全体の保護を変えることになります。よりきれいな方法は、Webhook のアドレスだけを通し、それ以外はそのままにするカスタムルールです。
- Security → Security rules を開き、新しいカスタムルールを作成します。
- 分かりやすい名前を付けます(例:Allow Stripe webhooks)。
- 条件を URI Query String、contains、
give-listener=stripeに設定します。 - アクションに Skip を選び、All remaining custom rules にチェックします。
- More components to skip を開き、Security Level と Browser Integrity Check にチェックします。
- ルールを先頭に配置してデプロイします。
(http.request.uri.query contains "give-listener=stripe")

見落としやすいのは手順 5 です。最初に作ったルールは Browser Integrity Check だけをスキップしており、API プレビューには "products": ["bic"] と表示され、このままでは Webhook は失敗し続けるところでした。イベントログはブロック元が Security level だと示していたので、スキップすべきはそのコンポーネントです。無料プランの注意点として、ログに Bot Fight Mode と表示される場合は Skip ルールでは回避できず、その設定自体をオフにする必要があります。
安全性はどうかというと、このルールはリクエストを無条件に信用するわけではありません。GiveWP は引き続き Stripe の署名シークレットですべての Webhook を検証するため、偽造されたリクエストは WordPress 側で拒否されます。Stripe を認証するのはもともと Cloudflare ではなく、署名の役割です。
修正を確認し、取りこぼした寄付を回復する
Stripe ダッシュボードから失敗したイベントを 1 件再送すると、200 OK が返ってきました。1 時間以内に Stripe の自動再試行も成功し始め、過去の失敗には Delivery recovered と表示されました。翌日には Stripe から endpoint が復旧したというメールも届きました。

後片付けでの実務的なポイントが 2 つあります。第一に、再送ボタンは各イベントの最新の配信にしか使えないため、Stripe がすでに諦めたイベントはイベント自体を開いてそこから再送します。第二に、照合すべきは Webhook ではなくお金です。障害開始以降の Stripe のすべての決済を WordPress の寄付記録と突き合わせました。今回は成功した決済はすべて記録済みで、残高不足で実際に失敗した 1 件は失敗として処理し、調査中の少額テスト決済 2 件は寄付として数えられないように明記しました。
決済を扱う WordPress サイトへの教訓
- まずレスポンス本文を読む。CDN の HTML を含む 403 はアプリの不具合ではなく、プラグインをいくらデバッグしても直りません。
- セキュリティ設定と決済 Webhook は整合させる。攻撃後にセキュリティレベルを上げるのは妥当ですが、サーバー間の通信にも効くことを忘れると寄付が消えていきます。
- 許可するのはパスだけ。受信アドレスに限定した Skip ルールなら、サイトの他の部分は保護されたままです。
- 期限切れのアドオンライセンスはゆっくり効いてくるリスク。更新が止まり、次のコア更新でアドオンが予告なく無効化されることがあります。
- アラートを見逃さない。Stripe は endpoint を無効にする前に約 1 週間警告を出します。そのメールが対応できる人に届くようにしておきましょう。
キャッシュと WordPress の時間が食い違った似た事例を キャッシュ TTL が原因の断続的な 403 エラーで、こうした問題を早期に見つける定期チェックについては Web サイト保守で実際に行うことで紹介しています。
よくある質問
自分のブラウザではサイトが見られるのに、なぜ Stripe Webhook は 403 になるのですか?
あなたのブラウザは Cloudflare のチャレンジを通過できても、Stripe のサーバーは通過できないからです。失敗したレスポンス本文に Just a moment や challenges.cloudflare.com が含まれていれば、リクエストは WordPress に届く前に Cloudflare で止められています。
Stripe の IP アドレスを許可リストに入れる方が良いですか?
Stripe が公開している IP リストを使えば可能ですが、リストは変わるため維持管理が必要です。Webhook パスに対する Skip ルールの方がシンプルで、プラグインが署名を検証するため実際の安全性も変わりません。
Webhook が失敗している間、寄付は失われますか?
通常、お金自体は Stripe が回収しますが、サイト側では記録されません。イベントが配信されるか手作業で修正するまで、寄付は保留中のままで、領収書も送られず、継続寄付の更新も記録から抜け落ちます。
Stripe はどのくらい再試行しますか?
本番モードでは、間隔を広げながら最大 3 日間です。endpoint の失敗が続くと Stripe から警告メールが届き、最終的に無効化されます。
WordPress で寄付、ネットショップ、会員制サービスを運営していて、よく分からない決済アラートが届いていませんか?こうした調査は、私たちの WordPress 開発と保守サービスの得意分野です。お気軽にご相談ください。何が問題で、直すには何が必要かをはっきりお伝えします。