PHP 8.1 下载文件时不能也不应设置 Accept-Encoding 请求头,因其由客户端发出、服务端仅可读取;必须禁用 zlib.output_compression 并正确设置 Content-Disposition 和 Content-Length。

PHP 8.1 下载服务器文件时,不需要、也不应该手动设置 Accept-Encoding 头。 这个请求头由客户端(浏览器或 curl 等工具)发出,PHP 作为服务端接收它,用于判断是否启用 gzip/deflate 压缩响应——但文件下载场景下,你通常要禁用压缩,而不是主动发这个头。
为什么不能在 PHP 下载逻辑里设 Accept-Encoding
因为 Accept-Encoding 是**请求头(Request Header)**,只能由发起 HTTP 请求的一方(如浏览器、curl 命令、前端 JS 的 fetch)设置。PHP 脚本运行在服务端,它读取这个头(例如通过 $_SERVER['HTTP_ACCEPT_ENCODING']),但无法“设置”它来影响自身响应行为。
- 你在下载脚本里写
header('Accept-Encoding: gzip')是无效的:服务端不能给自己发请求头,该 header 会被忽略或导致 HTTP 协议错误 - 真正起作用的是客户端是否带了这个头,以及服务端是否开启输出压缩(如
zlib.output_compression) - 对下载文件来说,启用压缩反而危险:二进制文件被 gzip 压缩后,
Content-Length会变,Content-Disposition行为可能异常,iOS Safari 等客户端会直接失败
下载文件前必须关掉 zlib 输出压缩
PHP 默认可能开启 zlib.output_compression(尤其在某些共享主机或旧配置中),这会导致下载文件被额外压缩一层,破坏原始二进制结构。
- 检查 php.ini:确认
zlib.output_compression = Off或zlib.output_compression = 0 - 运行时强制关闭(放在下载逻辑最开头):
if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', '0'); } if (function_exists('zlib_get_coding_type') && zlib_get_coding_type()) { // 已启动压缩,需清空缓冲并禁用 while (ob_get_level()) ob_end_clean(); } - 注意:
zlib.output_handler必须为空,否则和zlib.output_compression冲突
如何判断客户端是否支持压缩?不重要,下载时一律禁用
即使客户端发了 Accept-Encoding: gzip,deflate,你也**不该响应压缩内容**。原因很实际:
立即学习“PHP免费学习笔记(深入)”;
-
readfile()或fopen()+fpassthru()输出的是原始字节流,压缩层会把它再包一层,导致文件损坏 -
Content-Length变成压缩后长度,和真实文件大小不一致,Safari/iOS 会中断下载 - 浏览器无法解压 attachment 类型响应,最终用户拿到的是乱码 zip 文件
- 唯一安全做法:确保整个下载响应路径无任何输出缓冲或压缩介入
真正要盯紧的只有三件事:关 zlib、设对 Content-Disposition(含 filename*)、用 filesize() 算准 Content-Length。其他头都是干扰项。



















