Laravel需通过中间件手动解压gzip请求体:检测php://input以\x1f\x8b开头,用gzdecode解压后替换请求流,否则$request->all()为空;Accept-Encoding与请求体压缩无关,Nginx不处理此场景。

如何让 Laravel 正确解压 Content-Encoding: gzip 的请求体
Laravel 默认不处理 gzip 编码的请求体(request body),即使客户端发了 Content-Encoding: gzip,框架也会把压缩后的二进制数据原样当普通字符串读取——结果就是 $request->all() 为空、JSON 解析失败、file_get_contents('php://input') 返回乱码。
根本原因:PHP 的 php://input 流在 CGI/FPM 模式下**不会自动解压**请求体,Laravel 的 Illuminate\Http\Request 直接基于它构建,中间没做解压层。
- 仅对
POST/PUT/PATCH等含请求体的方法生效,GET请求无此问题 - 需确保 PHP 已启用
zlib扩展(绝大多数环境默认开启) - Apache/Nginx 不会自动解压请求体传给 PHP,这是应用层责任,不是服务器配置问题
在中间件里手动解压 php://input
最稳妥的做法是写一个全局中间件,在请求进入路由前检查并替换原始输入流。不能依赖 $_POST 或 $_FILES,因为它们对非 application/x-www-form-urlencoded 或 multipart/form-data 类型无效。
关键点:必须在 Request 实例创建前完成解压,否则后续所有读取(包括验证、参数解析)都基于错误数据。
- 在
app/Http/Middleware/DecompressRequestBody.php中定义中间件 - 在
handle()方法开头读取原始php://input,检测是否以\x1f\x8b开头(gzip 魔数) - 用
gzdecode()解压,成功后调用Stream::create()构造新流,并通过$request->instance()替换当前请求实例的底层流 - 注意:解压失败时应直接返回
400 Bad Request,避免静默失败
if (strpos($content = file_get_contents('php://input'), "\x1f\x8b") === 0) {
$decoded = @gzdecode($content);
if ($decoded === false) {
abort(400, 'Invalid gzip encoding');
}
$stream = \Illuminate\Http\Stream::create($decoded);
$request->setLaravelRequest($request->duplicate([], [], $stream));
}
Accept-Encoding: gzip 和请求体压缩是两回事
很多人混淆响应压缩和请求体压缩:Accept-Encoding: gzip 是告诉服务端“我可以接收 gzip 压缩的响应”,和服务端能否接收 gzip 请求体完全无关。Laravel 的 Response 压缩(如使用 CompressionMiddleware)不影响请求解析逻辑。
- 客户端必须显式设置
Content-Encoding: gzip请求头,并发送真实 gzip 二进制数据 - Postman / curl 发送时需用
--compressed+ 手动编码,例如:curl -H "Content-Encoding: gzip" --data-binary @body.gz ... - 前端用
fetch发送 gzip 数据需自行压缩(如用pako),浏览器不会自动压缩请求体 - 不要在 Nginx 层配置
gunzip on处理请求体——它只解压响应,且对非标准 header 可能失效
为什么不用 enable_post_data_reading 或修改 php.ini
PHP 没有提供开关让 php://input 自动解压,enable_post_data_reading=Off 只影响 $_POST 解析,不改变 php://input 内容;zlib.input_compression 这个 ini 选项在现代 PHP(7.4+)中已被废弃,且只作用于 CLI 模式,FPM 下无效。
- 试图用
stream_filter_append()给php://input加解压 filter 会失败,因为该流是只读且不可过滤的 - 重写
$_SERVER['CONTENT_LENGTH']或伪造php://memory流容易导致后续中间件或框架逻辑异常(比如上传文件大小校验崩溃) - 最简路径就是中间件里一次性解压、替换流——清晰、可控、不污染全局状态
真正麻烦的是测试:你得生成合法 gzip 二进制、构造带 Content-Encoding 头的请求,再验证 $request->json()->all() 能正确取出数据。漏掉魔数检测或错误处理,线上就收不到有效 payload。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











