Webhookの基本:外部サービスからの通知を受け取る仕組みと実務の確認ポイント
Webhookは、外部サービスで起きた出来事を、こちらのURLへ自動で知らせてもらう仕組みです。たとえば「決済が完了した」「GitHubにpushされた」「フォームが送信された」といったイベントを受け取り、WordPress更新、チャット通知、在庫更新、社内処理の起動につなげられます。
ポイントは、こちらから何度も確認しに行くのではなく、相手サービスからHTTPリクエストが送られてくることです。APIを使った定期取得よりも早く反応でき、無駄な問い合わせも減らせます。
この記事では、Webhookの考え方をWeb制作・WordPress・サーバー運用・自動化の実務に寄せて整理します。
- Webhookは「イベント発生時に外部サービスがURLへ通知する」仕組み
- 受け取り側には、HTTPSで公開されたエンドポイントが必要
- 実務では、署名検証・再送・重複処理・ログ確認が重要
- APIの定期実行とWebhookは役割が違うため、用途で使い分ける
Webhookで何ができるのか
Webhookを使うと、外部サービスの変化をきっかけに、自分のシステム側で処理を始められます。
身近な例では、次のような場面があります。
- フォーム送信を受け取って、Slackやメールに通知する
- Stripeなどの決済完了を受け取り、注文ステータスを更新する
- GitHubのpushやpull requestを受け取り、デプロイやテストを起動する
- 予約サービスの登録通知を受け取り、Googleスプレッドシートへ記録する
- WordPressの問い合わせフォーム送信を別システムへ渡す
ここで重要なのは、Webhookが「画面に通知を出す機能」そのものではない点です。Webhookはあくまで、サービス同士がHTTPでイベント情報を渡す入口です。その後に何をするかは、受け取り側のプログラムや自動化ツールで決めます。
Webhookの仕組みをHTTPで見る
Webhookの中身は、多くの場合、HTTPのPOSTリクエストです。
外部サービスは、設定されたURLに対してJSONなどのデータを送ります。受け取り側は、そのデータを読み取り、必要な処理を実行します。
基本の流れ
- 外部サービス側でイベントが発生する
- 外部サービスが、登録済みのWebhook URLへHTTPリクエストを送る
- 受け取り側のサーバーがリクエスト本文を読む
- 署名や秘密情報を確認する
- 注文更新、通知、保存、ジョブ起動などの処理を行う
- 正常に受け取ったことをHTTPステータスコードで返す
たとえばGitHubのWebhookでは、イベントごとにリクエストが送られ、ヘッダーやペイロードを使って内容を判断します。StripeのWebhookも、決済や請求などのイベントをエンドポイントへ送信する仕組みです。
ここがポイント: Webhookは「相手サービスがこちらのURLを呼び出す」ため、受け取り側のURLは外部から到達できる必要があります。ローカルPCだけで待ち受けても、そのままでは本番サービスから届きません。
APIの定期取得との違い
WebhookとAPIの定期取得は、どちらも外部サービスと連携する方法ですが、向いている場面が違います。
| 方法 | 動き方 | 向いている場面 | 注意点 |
|---|---|---|---|
| Webhook | イベント発生時に相手から通知される | 決済完了、フォーム送信、Git pushなど、すぐ反応したい処理 | 受信用URL、署名検証、再送対策が必要 |
| APIの定期取得 | こちらから一定間隔で問い合わせる | 日次集計、一覧同期、過去データの取得 | 取得間隔が長いと反映が遅れ、短いとAPI制限に当たりやすい |
Webhookはリアルタイム性に強い一方、取りこぼしや重複に備える必要があります。APIの定期取得は反応速度では劣りますが、過去データをまとめて確認しやすい利点があります。
実務では、Webhookでイベントを受け取り、必要に応じてAPIで詳細情報を取りに行く組み合わせもよく使われます。Webhookのペイロードだけで全情報を信頼して処理するより、イベントIDや対象IDを受け取り、APIで現在の状態を確認してから更新する形です。
最小構成の受け取り例
ここでは、PythonのFlaskでWebhookを受け取る最小例を示します。実務ではフレームワークやホスティング環境に合わせて変わりますが、「POSTを受ける」「JSONを読む」「ステータスを返す」という流れは共通です。
前提環境の例です。
- Python 3.10以降
- Flask
- HTTPSで公開できるサーバー、または開発用トンネルツール
- JSON形式のWebhookを送る外部サービス
サンプルコード
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.post("/webhook")
def receive_webhook():
payload = request.get_json(silent=True)
if payload is None:
return jsonify({"error": "invalid json"}), 400
event_type = payload.get("type")
event_id = payload.get("id")
print("event_type:", event_type)
print("event_id:", event_id)
return jsonify({"received": True}), 200
if __name__ == "__main__":
app.run(port=5000)
この例では、/webhook というURLでPOSTを受け取ります。外部サービスから次のようなJSONが届く想定です。
{
"id": "evt_12345",
"type": "payment.completed",
"data": {
"order_id": "order_789",
"amount": 5000
}
}
期待される出力は次のようになります。
event_type: payment.completed
event_id: evt_12345
外部サービス側には、公開URLとして次のようなエンドポイントを登録します。
https://example.com/webhook
ローカル開発中に試す場合は、そのまま http://localhost:5000/webhook を外部サービスへ登録しても届きません。相手サービスから見えるURLが必要です。開発用にはトンネルツールを使う方法がありますが、本番運用では独自ドメインとHTTPSを用意するのが基本です。
実務で最低限確認する設定
Webhookは設定画面でURLを入れるだけに見えますが、実際には受け取り側の設計で安定性が変わります。
受け取るイベントを絞る
多くのサービスでは、送信するイベント種類を選べます。最初から全イベントを受け取ると、ログが増え、想定外のデータ形式にも対応しなければなりません。
最初は、必要なイベントだけに絞ります。
- 決済完了だけが必要なら
payment.completedのような完了イベントを選ぶ - GitHub連携ならpushだけか、pull requestも必要かを分ける
- フォーム連携なら送信完了イベントだけで足りるか確認する
イベントを絞ると、コード側の分岐も読みやすくなります。
すぐ返して、重い処理は後ろに回す
Webhook送信元は、受け取り側からのHTTPレスポンスを見て成功・失敗を判断します。受け取り処理の中で重い処理を長く実行すると、タイムアウトや再送の原因になります。
実務では、次の流れに分けると扱いやすくなります。
- リクエストを受ける
- 署名と形式を確認する
- イベントIDと本文を保存する
- 早めに
200 OKを返す - 重い処理はキュー、バッチ、別ジョブで実行する
小規模な通知なら同期処理でも足ります。ただし決済や在庫更新のように失敗時の影響が大きい処理では、受信と実処理を分けた方が復旧しやすくなります。
セキュリティで必ず見るポイント
Webhook URLは外部に公開されます。URLを知っている人が勝手にPOSTできる可能性を前提に、受け取り側で確認が必要です。
署名検証を入れる
代表的なサービスでは、Webhookに署名ヘッダーやシークレットを使った検証方法が用意されています。GitHubではWebhook secretを設定し、送信された署名を検証できます。Stripeも署名付きイベントを検証する仕組みを提供しています。
署名検証を入れる目的は、本当にそのサービスから送られた通知かを確認することです。
最低限、次の点を守ります。
- Webhook secretはコードに直書きしない
.envや環境変数、シークレット管理機能で扱う- ログにAPIキー、署名値、個人情報をそのまま出さない
- 検証に失敗したリクエストは処理しない
HTTPSを使う
Webhookの本文には、注文ID、ユーザーID、メールアドレス、イベントIDなどが含まれることがあります。送信元サービスがHTTPSのURLだけを許可している場合もあります。
Web制作やWordPress運用では、SSL/TLS証明書の期限切れやリダイレクト設定も確認対象です。http:// から https:// へリダイレクトされる構成でも動く場合はありますが、サービスによってはリダイレクトを嫌うことがあります。Webhook登録URLは、最初からHTTPSの最終到達URLにしておく方が安全です。
よくある失敗と原因
Webhookが届かないときは、コードだけでなく、URL、サーバー、サービス側ログを順に見ます。
1. ローカルURLを登録している
localhost や 127.0.0.1 は自分のPCの中だけで通じる名前です。外部サービスからは到達できません。
改善策は、開発中なら一時的な公開URLを使い、本番ではHTTPS対応のサーバーに受信用エンドポイントを置くことです。
2. HTTPメソッドが違う
WebhookはPOSTで送られることが多いですが、受け取り側がGETだけに対応していると失敗します。
Flaskなら @app.post("/webhook")、Node.jsのExpressなら app.post("/webhook", ...) のように、POST用のルートを用意します。
3. 200番台を返していない
処理自体は成功していても、レスポンスが500や404になっていると、送信元は失敗と判断します。サービスによっては再送され、同じイベントが複数回届くことがあります。
受信ログには、少なくとも次を残すと調査しやすくなります。
- 受信日時
- イベントID
- イベント種別
- HTTPステータス
- 処理結果
- エラー理由。ただし秘密情報は除く
4. 同じイベントを二重処理している
Webhookは再送されることがあります。ネットワークの一時失敗、受け取り側のタイムアウト、レスポンス不備があると、同じイベントが再度届く可能性があります。
そのため、イベントIDを保存し、処理済みならスキップできるようにします。これを冪等性、つまり同じ処理を複数回実行しても結果が壊れない性質として扱います。
WordPressや自動化ツールで使う場合の考え方
WordPressやノーコード系の自動化では、自前でサーバーコードを書かずにWebhookを扱える場合があります。
たとえば、次のような選択肢があります。
- WordPressプラグインでフォーム送信を外部へ送る
- Power AutomateやZapierなどでWebhookを受け、メールやチャットに流す
- 自前のAPIエンドポイントを作り、WordPressからPOSTする
- GitHub ActionsやCI/CDでWebhook相当のイベントを起点に処理する
初心者が最初に迷いやすいのは、「受け取る側」と「送る側」のどちらを作っているのかです。
Webhookを受け取るなら、外部サービスが呼び出せるURLを用意します。Webhookを送るなら、相手が指定するURLに対して、必要な形式でHTTPリクエストを送ります。同じWebhookという言葉でも、立場が逆になると設定項目が変わります。
関連ツールとの使い分け
Webhookだけで全部を解決しようとすると、設計が苦しくなることがあります。目的に合わせて周辺の方法と組み合わせるのが実務的です。
- すぐ通知したい: Webhook
- 過去データをまとめて取りたい: REST API
- 定期的に同期したい: cron、スケジューラー、Power Automateの定期実行
- ブラウザ上の操作を自動化したい: RPAやブラウザ自動化
- 大量処理を安定させたい: キュー、ジョブワーカー、バッチ処理
Webhookは「きっかけ」を受け取る仕組みです。大量データの取得、長時間処理、手動確認が必要な業務までWebhookの受信処理だけに詰め込むと、タイムアウトや重複処理が起きやすくなります。
導入前のチェックリスト
最後に、実務でWebhookを設定する前に確認したい項目をまとめます。
- 受信用URLは外部からHTTPSで到達できるか
- POSTを受け取るルートになっているか
- 受け取るイベント種類を絞っているか
- 署名検証やシークレット確認を入れているか
- APIキーや個人情報をログに出していないか
- 同じイベントが再送されても二重処理しないか
- 失敗時のログを追えるか
- 重い処理を受信処理の中で長く実行していないか
- 本番と開発環境のWebhook URLを分けているか
Webhookを理解すると、外部サービス連携の見え方が変わります。単なる通知設定ではなく、「どのイベントを受け、どこまでを即時処理し、どこからを後続処理に回すか」を決める設計ポイントです。
次に見るべきなのは、使いたいサービスのWebhook署名検証と再送仕様です。そこを確認してから実装すると、届かない・二重処理される・本物か分からない、という初期トラブルをかなり減らせます。
