プッシュ通知が1通も届かない:APNs は HTTP/2 が必須で、PHP の curl が黙って HTTP/1.1 で送っていた

アプリ内のお知らせ(ベル)には入るのに、プッシュ通知が1通も届きませんでした。宛先も本文も送るタイミングも正しく、送信もしていました。本番のログには Unexpected HTTP/1.x request と出ていて、原因は、PHP の curl が HTTP/2 を指定されないまま HTTP/1.1 で送っていたことでした。

PHP の curl が HTTP/1.1 で送り、APNs が HTTP/2 を求めて status 0 になる流れの札

PHP から APNs(Apple のプッシュ通知の窓口)に通知を送っても届かず、status が 0 になる場合、原因は「HTTP/2 を指定していないこと」かもしれません。APNs は HTTP/2 でないと応答として読めません。私たちは、curl の設定で HTTP のバージョンを指定せず、本番では HTTP/1.1 で送られていました。HTTP/2 を明示し、使えない環境ではその理由を出す仕組みを足して直しました。

接客の録音アプリ(TalkLens)で、採点が終わると、スタッフに通知が届く作りです。ところが、実機で試したオーナーから、「アプリの中のベル(お知らせの一覧)には採点完了が入るのに、プッシュ通知が来ない」と報告がありました。この記事は、その原因を調べた記録です。デバイスの識別の文字列や、鍵の情報は書きません。

ベルには入るのに、プッシュ通知が来ない

アプリの中のベルには、採点完了が入っていました。つまり、サーバーは通知を作っています。それでも、iPhone のロック画面には何も出ませんでした。

れんがの壁についた、真ちゅうの呼び鈴のボタンの写真
ベルには入るのに、プッシュ通知は鳴らなかった(イメージ写真)
アプリ内のお知らせのベルには採点完了が入るのに、プッシュ通知がロック画面に届かない。宛先も本文も送るタイミングも正しく、送信もしていた
通知を作る部分は、正しく動いていた

本番のログに、Unexpected HTTP/1.x request と並んでいた

次に、本番のログを見ました。

本番のログの1行。APNs responded 0 のあとに Unexpected HTTP/1.x request: POST /3/device/ と出ている。デバイスの識別の文字列は伏せている
送り方を、窓口が受け付けていなかった(識別の文字列は伏せている)
テキストサーバーのログの1行(識別の文字列は省略)
APNs responded 0: ... Unexpected HTTP/1.x request: POST /3/device/...

「HTTP/1.x のリクエストは予期していない」という意味の文が、並んでいました。通知の送り先の窓口が、私たちの送り方を、受け付けていなかったのです。

原因:HTTP のバージョンを指定せず、HTTP/1.1 で送っていた

私たちの PHP の送信の部品は、curl の HTTP のバージョンを指定せず、curl が相手と話し合って決める(ALPN というネゴシエーション)のに任せていました。本番のサーバーでは、その結果、HTTP/1.1 で送られていました。

赤い看板に「WRONG WAY」と書かれた写真
HTTP のバージョンを指定せず、違う道を通っていた(イメージ写真)
PHP の curl が HTTP のバージョンを指定せず、本番では HTTP/1.1 で送られ、HTTP/2 を必須とする APNs が応答として読めずに status が 0 になる流れ
エラーの形から、原因が読み取りにくい

直し方:HTTP/2 を明示し、使えない環境では理由を出す

直し方の3つ。CURL_HTTP_VERSION_2_0 を明示する、HTTP/2 を使えない環境では理由つきの例外を投げる、送る前に curl の HTTP/2 対応を表示する
指定するだけでなく、確かめる
PHPAPNs に送る時の指定(仕組みが分かる短い見本)
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);  // APNs は HTTP/2 が必須

レンタルサーバーで、確かめておくこと

レンタルサーバーの PHP と curl は、サーバーによって作り方が違います。

nghttp2 を含む curl は HTTP/2 で送れるが、nghttp2 無しで作られた curl は HTTP/2 を指定しても黙って HTTP/1.1 で送る。環境の curl が HTTP/2 に対応しているかを実際に確かめる
「指定したから大丈夫」ではない
  1. curl が HTTP/2 に対応しているか確かめる

    PHP から curl_version() を呼ぶと、対応している機能の一覧が見られます。HTTP2 の項目があるかを確かめます。

  2. 送る前に、対応を表示する仕組みを入れる

    通知を送る道具の最初に、HTTP/2 への対応を表示させます。対応していなければ、そこで止めます。

  3. 実機に通知が届くまで、ログを見る

    status が 0 のままなら、HTTP のバージョンを疑います。

まとめ

プッシュ通知が1通も届かない:APNs は HTTP/2 が必須で、PHP の curl が黙って HTTP/1.1 で送っていた:要点のまとめ。ベルには入るのにプッシュ通知が来ない時、宛先・本文・送信のタイミングが正しくても、HTTP のバージョンを疑う
この記事の要点
  • ベルには入るのにプッシュ通知が来ない時、宛先・本文・送信のタイミングが正しくても、HTTP のバージョンを疑う
  • APNs は HTTP/2 が必須。HTTP/1.1 で送ると、status が 0 になり、原因が読み取りにくい
  • 本番のログに Unexpected HTTP/1.x request が並んでいた
  • CURL_HTTP_VERSION_2_0 を明示する。nghttp2 無しの curl は、指定しても黙って HTTP/1.1 になるので、対応を実際に確かめる

この記事は、AI(Claude)の支援を受けて作成し、筆者が内容を確認しています。

  • APNs
  • プッシュ通知
  • HTTP/2
  • PHP
  • curl
  • レンタルサーバー

関連する記事

この記事に関連するサービス・アプリ

ブログの一覧へ