You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

  1. 更新系统包并安装依赖
    sudo apt update && sudo apt install -y libcurl4-openssl-dev php8.1-curl
    
  2. 重启PHP-FPM服务
    sudo systemctl restart php8.1-fpm
    
  3. 验证HTTP2支持
    • 执行以下PHP代码验证:
      <?php
      $curlVersion = curl_version();
      echo ($curlVersion['features'] & CURL_VERSION_HTTP2) ? "HTTP2 已启用" : "HTTP2 未启用";
      ?>
      
    • 或查看phpinfo()中cURL模块下的HTTP2字段是否为Yes。

第二步:修正APNs推送代码

代码问题点及修复

  1. JWT编码错误:原代码使用普通base64()编码,不符合JWT要求的URL安全Base64规则,需替换为自定义编码函数。
  2. 未强制HTTP2协议:需明确指定cURL使用HTTP2发送请求。
  3. 签名算法不匹配:ES256对应ECDSA-SHA256,需修正签名算法参数。
  4. 冗余配置:移除不必要的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 09:24:55