必须显式设置CURLOPT_ENCODING为''才能自动解压gzip响应,否则返回乱码;file_get_contents不支持自动解压,需改用cURL或手动gzdecode;调试应结合curl_getinfo验证响应头与magic bytes。

curl_setopt 里必须显式启用 CURLOPT_ENCODING
PHP 的 curl_setopt 默认不会自动处理 gzip 响应解压,即使服务端返回了 Content-Encoding: gzip。不设 CURLOPT_ENCODING,你收到的就是一串乱码二进制数据,json_decode 直接失败,file_get_contents 也读不出有效文本。
正确做法是传空字符串给 CURLOPT_ENCODING,让 cURL 自动协商支持的压缩方式:
curl_setopt($ch, CURLOPT_ENCODING, '');
- 传
''(空字符串):cURL 自动在请求头加Accept-Encoding: gzip, deflate,并自动解压响应体 - 传
'gzip':只声明支持 gzip,但部分旧版 cURL 可能不自动解压,需手动调用gzdecode() - 不要传
'identity'或忽略该选项,否则压缩响应原样返回
POST 数据本身是否 gzip 压缩由服务端决定
所谓“PHP 模拟 POST 请求 gzip 压缩”,通常指两种情况:一种是发请求时把 POST body 压缩(服务端主动要求),另一种是收响应时解压(更常见)。绝大多数 API(如微信、支付宝、云厂商接口)只要求客户端接收 gzip 响应,**不要求你发 gzip body**。
如果你真遇到服务端明确要求上传 gzip 压缩的 JSON 或表单数据(极少见),需手动压缩并设置请求头:
立即学习“PHP免费学习笔记(深入)”;
$data = gzencode(json_encode($payload), 9);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Encoding: gzip',
'Content-Length: ' . strlen($data)
]);
- 务必用
gzencode()(不是gzcompress()或gzdeflate()),它生成标准 gzip 格式 - 必须显式设置
Content-Length,因为gzencode后长度变了 - 很多服务端不校验或不支持接收 gzip body,先确认文档再动手
file_get_contents 不支持自动解压 gzip 响应
别试图用 stream_context_create 配 http 选项来让 file_get_contents 解 gzip。PHP 的流封装器对 Accept-Encoding 支持极弱,file_get_contents 返回的永远是原始响应体,哪怕 header 里写了 Content-Encoding: gzip。
这是硬限制,不是配置问题。要可靠解压,只有两个选择:
- 改用
curl并设CURLOPT_ENCODING => '' - 手动接收 raw body 后用
gzdecode()(注意:仅适用于 PHP ≥ 5.4,且响应确实是 gzip 格式)
例如手动解压:
$raw = file_get_contents($url, false, $context);
if (strpos($http_response_header[0], '200') !== false && preg_grep('/^Content-Encoding:.*gzip/i', $http_response_header)) {
$body = @gzdecode($raw);
}
调试时 curl_getinfo 要看真实响应头
光看 curl_exec 返回的内容不够,压缩是否生效、服务端是否真的返回了 gzip,得查响应头。用 curl_getinfo($ch, CURLINFO_HEADER_OUT) 看请求头是否带 Accept-Encoding,用 curl_getinfo($ch, CURLINFO_CONTENT_TYPE) 和 CURLINFO_CONTENT_ENCODING 看服务端实际返回了什么。
-
CURLINFO_CONTENT_ENCODING返回gzip才说明服务端确实压缩了 - 如果返回空,但响应体开头是
\x1f\x8b(gzip magic bytes),说明服务端压缩了但没写 header —— 这属于服务端 bug,你得手动gzdecode -
curl_setopt($ch, CURLOPT_HEADER, true)会把 header 和 body 混在一起,不方便解析,优先用curl_getinfo
gzip 响应体的边界很模糊:header 可能缺失、压缩级别不一致、服务端中间件可能提前解压又不重写 header。最稳妥的方式,始终以实际字节流特征(magic bytes) + Content-Encoding header + 文档约定三者交叉验证。











