← 全記事
チュートリアル

LINE WebhookのVerifyが失敗する?空のeventsを正しく処理する

LINEのWebhook URL検証では、events配列が空のHTTP POSTリクエストが届きます。受信側は署名を検証して200を返し、メッセージの読み取りや返信は行いません。events[0]が必ず存在すると仮定した実装では、通常のメッセージを処理できてもVerifyに失敗することがあります。逆に、Verifyの成功だけでは、実際のメッセージがキューや問い合わせ受信箱に届くことまでは確認できません。

要点

  • 正しく認証されたevents: []は通信確認であり、メッセージの欠落ではありません。
  • JSONを解釈する前に、チャネルシークレットと元のリクエストボディでx-line-signatureを検証します。
  • 通信確認と業務処理のテストは分けます。
  • LINEのネイティブWebhookとUnifyPortの正規化イベントは別の仕様です。

Verifyボタンで届くリクエスト

公式のWebhook URLを検証するガイドには、WebhookテストAPIとLINE DevelopersコンソールのVerifyボタンという2つの確認方法があります。いずれもLINEと受信サーバーが通信できるかを確認します。イベントを含まない確認リクエストには、200を返すよう定められています。

以下はLINEの署名検証例で使われているボディの形式です。顧客メッセージではありません。

{"destination":"U8e742f61d673b39c7fff3cecb7536ef0","events":[]}

メッセージ、送信者、返信トークンを持つイベントオブジェクトは存在しません。destinationから顧客レコードを作ったり、メッセージIDを生成したり、自動返信を登録したりしないでください。運用監視が必要なら、通信確認を顧客のメッセージ数とは別に記録します。

URLを変更する前に、現在の受信先の管理者を確認しましょう。同じLINE公式アカウントに複数ツールを接続している場合は、複数ツール利用時のWebhookチェックリストを参照してください。パーサーの不具合を調べるために、正常な受信先から通信を移してしまわないことが重要です。

署名検証の後に空配列を処理する

LINEの署名検証ガイドでは、受信したボディを入力、チャネルシークレットを鍵としてHMAC-SHA256を計算し、Base64形式の署名を比較します。このシークレットは、送信APIに使うチャネルアクセストークンとは異なります。

受信時のバイト列をそのまま保持してください。JSONを解析して再シリアライズすると、空白やエスケープ表現が変わり、署名が一致しなくなる場合があります。空配列でも認証は必要です。認証されていない送信元も同じJSON形式を作れるためです。

次のNode.js関数は、LINEネイティブのリクエストを検証して解析する実装例です。rawBodyにはHTTPフレームワークが保持した元のBufferを、channelSecretにはサーバー側で安全に管理した値を渡します。完全なサーバーや永続キューの実装ではありません。

import { createHmac, timingSafeEqual } from 'node:crypto';

export function readLineEvents(rawBody, signature, channelSecret) {
  if (!Buffer.isBuffer(rawBody) || !channelSecret) {
    throw new Error('Raw body and channel secret are required');
  }
  const expected = createHmac('sha256', channelSecret)
    .update(rawBody)
    .digest('base64');
  const supplied = Buffer.from(signature ?? '', 'utf8');
  const calculated = Buffer.from(expected, 'utf8');
  if (supplied.length !== calculated.length ||
      !timingSafeEqual(supplied, calculated)) {
    return { status: 401 };
  }
  try {
    const payload = JSON.parse(rawBody.toString('utf8'));
    if (!payload || !Array.isArray(payload.events)) {
      return { status: 400 };
    }
    return { status: 200, events: payload.events };
  } catch {
    return { status: 400 };
  }
}

400と401は、この例が防御的な処理として選んだレスポンスであり、LINE APIのエラーコードではありません。呼び出し側は次のように処理します。

  1. x-line-signatureヘッダーと変更していないボディを関数に渡す。
  2. 無効なリクエストを拒否し、業務タスクを作らない。
  3. 検証に成功し、events.length === 0ならHTTP 200を返す。
  4. 空でなければ、イベントを永続化してから受信を確認し、時間のかかる処理を非同期で実行する。保存に失敗した場合、関数が返したステータスだけを理由に200を送らない。

LINEはイベントの非同期処理を推奨しています。永続化とワーカーの設計はアプリケーション側の責任です。また、先頭要素だけでなく配列全体を処理してください。

失敗した箇所を切り分ける

観測した状態次に確認する項目
アプリにリクエストが来ない設定したホストとパス、TLS、入口のルーティング、プロキシログ
署名検証に失敗するチャネルシークレット、元のバイト列の保持、ヘッダー転送
検証済みの空リクエストで例外になる先頭イベント、メッセージ、返信トークンへの無条件アクセス
アプリは成功と記録するがVerifyは失敗する公開入口が実際に返すステータスと処理時間
Verifyは成功するが受信箱に届かない実イベントの保存、ルーティング、ワーカーの実行

空配列を処理できないだけで、認証情報を変更したり、Webhookの管理先を移したりしないでください。実イベントは届くのに返信トークンがない場合は別の問題です。LINE standbyモードの解説は返信権限を扱っています。空の検証リクエストには、確認すべきイベントのmode自体がありません。

通信とメッセージ処理を別々に確認する

以下は推奨する受け入れテストであり、実施済みの本番結果ではありません。

  • 正しい署名の空リクエストに200を返し、顧客メッセージや送信タスクを作らない。
  • 同じ空ボディでも、署名なし・署名不一致なら拒否する。
  • 正しい署名の非空リクエストは、受信確認の前に永続化する。
  • Verify成功後、対象のLINE公式アカウントにテストメッセージを送り、予定した受信箱の処理を確認する。
  • 保存に失敗した処理を、署名が正しいという理由だけで完了扱いにしない。

診断には、署名検証結果、イベント数、HTTPステータス、観測時刻など必要な情報だけを残します。チャネルシークレット、返信トークン、顧客の本文を通常ログに記録しないでください。

UnifyPortの受信仕様とは分ける

UnifyPortの非公式インターフェースは、LINE認証フローでLINEアカウントを接続します。LINE公式アカウントのネイティブWebhook検証サービスではありません。

Webhook配信仕様ではX-Device-Signatureを使います。signing_secretを設定すると、X-Device-Timestamp、ドット、元のボディを連結した値に対するHMAC-SHA256を16進数形式で署名します。正規化エンベロープはid、type、provider、account_id、occurred_at、dataで構成され、LINEのevents配列ではありません。

別ルート、または明示的に分けた受信設定を使ってください。UnifyPortのペイロードにネイティブのeventsがないことを、LINEの空検証リクエストと解釈してはいけません。LINE Verifyの成功はUnifyPortの配信、アカウント認証、正規化メッセージ処理を検証しません。LINE公式アカウントとしての動作が必要なら、公式Messaging APIを維持してください。

FAQ

events: []はエラーですか?

LINEのWebhook URL検証では正常です。署名検証後に200を返し、メッセージタスクを作りません。フィールドの欠落や型の不一致は、空配列とは別です。

通常のメッセージは処理できるのにVerifyが失敗するのはなぜですか?

すべてのリクエストに先頭のメッセージイベントがあると仮定する実装が原因になり得ます。実際のレスポンスと例外ログを確認してください。通信や署名の問題でも失敗します。

検証成功で返信機能まで確認できますか?

できません。受信先との通信確認であり、保存、ワーカー、返信可能条件、送信APIの成功は別途テストが必要です。

次の手順と参考資料

LINE公式アカウントでは、Webhook URL検証を行ってから、実際のテストメッセージを追跡してください。独立したUnifyPort接続では、文書化されたWebhook仕様に従って実装します。

公式資料の確認日:2026-10-28。

UnifyPort API

メッセージ連携を安定したプロダクトパイプラインへ。

まずは 1 つの API で送信を始め、標準イベントですべての inbound メッセージを業務システムへ戻しましょう。