把站点从HTTP迁移到HTTPS后,PHP脚本突然接收不到任何请求数据,$_POST、$_GET甚至php://input全是空的,这种情况在运维和开发中都相当常见。问题的根源往往不在PHP代码本身,而在SSL证书配置、Web服务器转发规则或证书链校验环节。本文按照实际排查顺序,把可能出现故障的环节逐一拆开分析,并给出可直接使用的检测命令和修复方案。

一、先确定问题出在客户端还是服务端
排查的第一步是弄清楚:是服务端根本没收到请求,还是收到了但PHP层拿不到数据。两者原因完全不同,处理方向也不同。可以用以下命令在服务器本机测试443端口是否通:
# 在服务器本机测试443端口 curl -v https://www.ipipp.com/test.php -d "name=tom" # 只测试端口连通性 telnet 127.0.0.1 443
如果本机curl能正常返回数据,而外部请求失败,那问题大概率在防火墙、安全组或者CDN层。登录云控制台检查安全组规则是否放行了443端口,这是最容易被忽略的一步。很多人证书配置了半天,最后发现是安全组没开443,白白浪费几个小时。
如果本机curl也失败,就要看Web服务器(Nginx或Apache)的错误日志。Nginx默认日志在/var/log/nginx/error.log,重点找类似 SSL_do_handshake failed 的报错,这通常意味着证书文件路径错误、私钥不匹配,或者证书链不完整。
二、检查SSL证书本身是否有问题
证书问题是最常见的元凶,主要包括三种情况:证书过期、证书链不完整、域名不匹配。可以用openssl命令快速验证证书状态:
# 查看证书有效期和域名信息 openssl s_client -connect www.ipipp.com:443 -servername www.ipipp.com 2>/dev/null | openssl x509 -noout -dates -subject # 验证证书链是否完整(verify error说明链不完整) openssl s_client -connect www.ipipp.com:443 -servername www.ipipp.com
如果输出中出现 verify error: unable to get local issuer certificate,说明服务器没有发送完整的证书链。很多证书颁发机构会给一个中间证书文件,必须和域名证书拼接在一起配置。Nginx中的正确写法是把中间证书追加到证书文件末尾:
server {
listen 443 ssl;
server_name www.ipipp.com;
# 证书文件中应包含域名证书和中间证书
ssl_certificate /etc/nginx/ssl/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
}
另外一个高频坑点是证书与私钥不匹配。可以用下面两条命令对比 modulus 的MD5值,不一致就必须重新签发或找到正确的私钥:
openssl x509 -noout -modulus -in fullchain.crt | openssl md5 openssl rsa -noout -modulus -in private.key | openssl md5
顺带提醒一句,如果站点配置了多个HTTPS域名但Nginx没有正确设置 server_name,可能出现SNI回落错误——客户端拿着A域名的请求打到了B域名的证书上,浏览器直接报证书不匹配,某些PHP客户端库也会因此中断连接。
三、PHP作为客户端发起HTTPS请求失败的排查
另一种常见场景是:PHP脚本本身作为客户端去请求第三方HTTPS接口,一直返回false或空字符串。这时的关键点在于本地CA证书包是否可用。先确认php.ini中的配置:
; 证书包路径,留空会导致校验失败 openssl.cafile = /etc/pki/tls/certs/ca-bundle.crt curl.cainfo = /etc/pki/tls/certs/ca-bundle.crt
用cURL发请求时,不要图省事关闭证书校验(即设置CURLOPT_SSL_VERIFYPEER为false),这会带来中间人攻击风险,正确做法是指定CA证书路径:
<?php
$ch = curl_init('https://api.ipipp.com/v1/user');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query(['id' => 100]),
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_CAINFO => '/etc/pki/tls/certs/ca-bundle.crt',
CURLOPT_TIMEOUT => 10,
]);
$result = curl_exec($ch);
if ($result === false) {
// 打印具体错误,60号错误即证书校验失败
echo 'cURL错误(' . curl_errno($ch) . '): ' . curl_error($ch);
}
curl_close($ch);
如果用的是file_get_contents,则需要通过stream context传递SSL选项:
<?php
$context = stream_context_create([
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
'cafile' => '/etc/pki/tls/certs/ca-bundle.crt',
],
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query(['id' => 100]),
],
]);
$body = file_get_contents('https://api.ipipp.com/v1/user', false, $context);
两个方案各有优劣:cURL功能更全,错误信息更明确,还能获取具体的错误码;file_get_contents写法简洁但排查手段有限,遇到问题只能靠错误抑制符和日志。生产环境建议统一用cURL,并且把curl_errno的返回值记录到日志里,错误码60代表证书问题,28代表超时,35代表SSL握手失败,根据错误码定位效率会高很多。
四、服务端PHP拿不到POST数据的转发问题
请求确实到达了服务器,但PHP里$_POST为空,这时要检查Web服务器的转发配置。Nginx反代到PHP-FPM或后端PHP服务时,如果丢失了请求头,POST体就不会被传递:
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
# 这两行是关键,缺失会导致PHP拿不到POST数据
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
include fastcgi_params;
}
还有一种情况是Content-Type为application/json时,PHP不会自动解析请求体到$_POST,必须手动读取输入流,不少接口联调失败就是因为这个:
<?php
// JSON请求体需要手动解析
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
exit('非法的JSON格式');
}
最后,如果经过了负载均衡或CDN,证书可能在这些层已经卸载,后端收到的是HTTP请求。此时PHP通过$_SERVER['HTTPS']判断协议会失效,需要在转发时带上 X-Forwarded-Proto 头,并在PHP侧信任该头判断真实协议,否则重定向和Cookie的secure标记都会出问题。按照上面四个环节逐层排查,绝大多数HTTPS下的PHP请求问题都能快速定位到根因。
PHP HTTPS请求SSL证书配置接收不到请求排查修改时间:2026-09-12 07:18:33