PHP中cURL调用APNs失败排查(AWS EC2、NGINX、PHP8.1环境)
解决AWS EC2上PHP cURL推送APNs失败问题
问题分析
你遇到的Unexpected HTTP/1.x request错误,本质是APNs服务器强制要求使用HTTP2协议,但当前EC2实例上的PHP cURL未启用HTTP2支持,导致请求以HTTP1.x发送被拒绝。同时代码中存在JWT生成的编码错误,也会引发请求验证失败。
第一步:让PHP cURL支持HTTP2
操作步骤(Ubuntu 22.04)
- 更新系统包并安装依赖
sudo apt update && sudo apt install -y libcurl4-openssl-dev php8.1-curl - 重启PHP-FPM服务
sudo systemctl restart php8.1-fpm - 验证HTTP2支持
- 执行以下PHP代码验证:
<?php $curlVersion = curl_version(); echo ($curlVersion['features'] & CURL_VERSION_HTTP2) ? "HTTP2 已启用" : "HTTP2 未启用"; ?> - 或查看phpinfo()中
cURL模块下的HTTP2字段是否为Yes。
- 执行以下PHP代码验证:
第二步:修正APNs推送代码
代码问题点及修复
- JWT编码错误:原代码使用普通
base64()编码,不符合JWT要求的URL安全Base64规则,需替换为自定义编码函数。 - 未强制HTTP2协议:需明确指定cURL使用HTTP2发送请求。
- 签名算法不匹配:ES256对应ECDSA-SHA256,需修正签名算法参数。
- 冗余配置:移除不必要的
CURLOPT_HTTP09_ALLOWED配置。
修正后的完整代码
// 自定义URL安全Base64编码函数(JWT强制要求) function base64url_encode($data) { return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); } $keyfile = public_path("/ios/AuthKey_7QL8QAY5X2.p8"); $keyid = "something"; $teamid = "something"; $bundleid = "something"; $url = "https://api.push.apple.com"; $token = $deviceToken; $payload = json_encode([ 'aps' => [ 'content-available' => 1, 'alert' => [ 'title' => $title, 'body' => $message, ], 'sound' => 'default', 'badge' => 0, ] ]); // 加载APNs私钥 $key = openssl_pkey_get_private("file://" . $keyfile); if (!$key) { throw new Exception("无法加载APNs私钥: " . openssl_error_string()); } // 生成JWT头部与声明 $header = ["alg" => "ES256", "kid" => $keyid]; $claims = ["iss" => $teamid, "iat" => time()]; $header_encoded = base64url_encode(json_encode($header)); $claims_encoded = base64url_encode(json_encode($claims)); // 生成JWT签名 $signature = ""; $signSuccess = openssl_sign( $header_encoded . "." . $claims_encoded, $signature, $key, OPENSSL_ALGO_SHA256 // 对应ES256算法 ); if (!$signSuccess) { throw new Exception("JWT签名失败: " . openssl_error_string()); } $signature_encoded = base64url_encode($signature); $jwt = $header_encoded . "." . $claims_encoded . "." . $signature_encoded; // 初始化cURL请求 $http2ch = curl_init("$url/3/device/$token"); // 强制使用HTTP2协议 curl_setopt($http2ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); curl_setopt($http2ch, CURLOPT_PORT, 443); curl_setopt($http2ch, CURLOPT_HTTPHEADER, [ "apns-topic: {$bundleid}", "authorization: bearer $jwt", "apns-push-type: alert", "Content-Type: application/json" // 明确指定请求内容类型 ]); curl_setopt($http2ch, CURLOPT_POST, true); curl_setopt($http2ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($http2ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($http2ch, CURLOPT_TIMEOUT, 30); curl_setopt($http2ch, CURLOPT_HEADER, 1); curl_setopt($http2ch, CURLOPT_SSL_VERIFYPEER, true); // 生产环境必须开启SSL验证 $result = curl_exec($http2ch); if (curl_errno($http2ch)) { $error_msg = curl_error($http2ch); Log::error("APNs推送错误: " . $error_msg); } if ($result === false) { throw new Exception("Curl请求失败: " . curl_error($http2ch)); } curl_close($http2ch);
额外检查项
- 确保EC2实例安全组允许出站443端口(APNs依赖该端口通信)。
- 确认APNs私钥文件权限正确(PHP进程需可读,建议设置为
644)。 - 在aaPanel的
软件管理 -> PHP管理 -> 扩展中,确保PHP 8.1的cURL扩展已启用。
内容的提问来源于stack exchange,提问作者Esmatullah Arifi
相关产品推荐
相关产品推荐

