Discord の Webhook 配信の仕組み

Interactions は 3 秒以内の最初の応答を求め、署名には HMAC ではなく Ed25519 を使います。検証済みの署名形式と Web Crypto の検証コードを掲載しています。

配信の挙動

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

応答期限3 秒

Interaction エンドポイントは 3 秒以内に最初の応答を返す必要がある

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

リトライプロバイダーは未公表
打ち切りプロバイダーは未公表
出典https://docs.discord.com/developers/interactions/receiving-and-responding

署名

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

アルゴリズムEd25519
署名対象{timestamp}{body}
エンコードhex
ヘッダーx-signature-ed25519 + x-signature-timestamp
許容範囲適用なし

検証する

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

// Ed25519, not HMAC: verify with the application's public key (hex).
const hex = (s) => Uint8Array.from(s.match(/../g), (b) => parseInt(b, 16));

const key = await crypto.subtle.importKey(
  "raw",
  hex(publicKey), // Developer Portal → Application → Public Key
  { name: "Ed25519" },
  false,
  ["verify"],
);
const valid = await crypto.subtle.verify(
  "Ed25519",
  key,
  hex(req.headers["x-signature-ed25519"]),
  new TextEncoder().encode(req.headers["x-signature-timestamp"] + rawBody),
);

Discord は不正な署名でエンドポイントを積極的に試し、それが通ってしまう URL は拒否します。そのため検証処理を省いたスタブでは、セキュリティレビューだけでなく、登録そのものに失敗します。

落とし穴

3 秒の応答期限は、最初のインタラクション応答に適用されます。抜け道は type 5(deferred)です。期限内に応答だけを返し、実際の処理が終わってからフォローアップします。

あわせて読む

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

仕組みを見る →