Shopify の Webhook 配信の仕組み

応答期限は 5 秒で、4 時間にわたり 8 回リトライした後、Shopify はサブスクリプションを削除します。検証済みの数値、署名形式、Node の検証コードを掲載しています。

配信の挙動

プロバイダーの公式ドキュメントから読み取っています(最終確認 2026-08-06)。プロバイダーが数値を示していない項目には、この表も数値を載せていません。

応答期限5 秒

接続に 1 秒、リクエスト全体で 5 秒

ハンドラーが期限内に応答するか確認する

リトライ4 時間にわたり 8 回リトライ
打ち切りAdmin API で作成したサブスクリプションは、8 回連続で失敗すると削除される。アプリの緊急時の開発者連絡先には警告メールが届く
出典https://shopify.dev/docs/apps/build/webhooks/subscribe/https

署名

ドキュメントを読んだだけではなく、テストスイートで裏付けた動作する検証コードで確認しています。最終検証日は 2026-08-20 です。

アルゴリズムHMAC-SHA256
署名対象raw body
エンコードbase64
ヘッダーx-shopify-hmac-sha256
許容範囲適用なし

検証する

JSON としてパースする前の生のリクエストボディを、バイト単位でそのまま使います。フレームワークがペイロードを再シリアライズした時点で、このページのどの方式も検証に失敗します。

const crypto = require("node:crypto");

// base64(HMAC-SHA256(secret, raw body))
const expected = crypto
  .createHmac("sha256", hmacSecret) // admin webhooks: Settings → Notifications
  .update(rawBody)
  .digest("base64");
const received = Buffer.from(req.headers["x-shopify-hmac-sha256"] ?? "");
const wanted = Buffer.from(expected);
// timingSafeEqual throws on a length mismatch, so check that first.
const valid =
  received.length === wanted.length && crypto.timingSafeEqual(received, wanted);

シークレットは 2 種類あります。管理画面で作成した Webhook は Settings → Notifications にある共有シークレットで、アプリが作成した Webhook はアプリのクライアントシークレットで署名されます。Webhook の作成方法に合わせてください。

落とし穴

Shopify はサブスクリプションを無効化するのではなく、削除します。Admin API で作成したサブスクリプションは、8 回連続で失敗すると削除されます。アプリの緊急開発者連絡先に警告メールが届くだけで、どこにもエラーは出ず、注文がただ届かなくなります。取りこぼしたイベントはそもそも送信されていないので、復旧には Admin API との照合ジョブが必要です。

あわせて読む

AnyHook は、Shopify から受信するエンドポイントの手前に入ります。約 1 秒で応答し、サーバーがダウンしていれば独自のスケジュールでリトライします。すべてのイベントは再送できる状態で保存されます。 変えるのは URL 1 つだけで、コードはそのままです。

仕組みを見る →