php脚本必须显式处理range请求并返回206响应,nginx需禁用proxy_buffering、透传range头,且php输出缓冲须关闭;否则断点续传失效。

phpEnv 默认的 Nginx + PHP-FPM 组合不自动支持断点续传,必须手动补全 Accept-Ranges 响应头、正确处理 Range 请求,并禁用可能干扰流式响应的缓冲机制。否则浏览器会提示“无法恢复下载”或直接失败。
PHP 脚本里必须显式支持 Range 请求
PHP 默认不解析 HTTP_RANGE,也不会返回 206 Partial Content,哪怕 Nginx 配置再完善,后端脚本没处理,断点续传就形同虚设。
- 必须检查
$_SERVER['HTTP_RANGE']是否存在,不能只靠 Nginx 透传 -
fseek($fp, $range_start)前要确保文件可随机读(rb模式),且$range_start是合法整数(需用intval()强转,避免字符串注入) - 返回
Content-Range时格式必须严格为bytes 0-1023/1024,结尾不能多空格或换行,否则 Chrome 会拒绝恢复 - 务必调用
ob_end_clean()或while (ob_get_level()) ob_end_clean();,否则输出缓冲区残留内容会导致Content-Length错误
Nginx 需关闭 proxy_buffering 并透传 Range 头
phpEnv 中 Nginx 若反向代理到 PHP-FPM(常见于非 fastcgi_pass 直连场景),默认开启 proxy_buffering on,它会缓存整个响应体,导致分块响应失效,206 变成 200,断点中断。
- 在对应
location块中添加:proxy_buffering off; - 确保
proxy_set_header Range $http_range;和proxy_pass_request_headers on;已启用 - 不要设置
fastcgi_buffering on(该指令仅 Nginx 1.19+ 支持,phpEnv 多为旧版,实际无效) - 若使用
fastcgi_pass(更常见),则重点检查fastcgi_ignore_client_abort off;,设为on会导致连接中断时 PHP 进程被杀,影响大文件传输稳定性
别用 fpassthru() 或 readfile() 做断点续传
这两个函数会忽略当前文件指针位置,直接从开头输出,导致断点请求返回完整文件,客户端校验失败。
- 必须用
fopen()+fseek()+ 循环fread()手动控制字节偏移 - 每次
fread()后立即echo并flush(),但注意:PHP-FPM 的buffer模式下flush()无效,需确认 php.ini 中output_buffering = Off或设为0 - 示例关键片段:
$fp = fopen($filePath, 'rb'); fseek($fp, $start); while (!feof($fp) && (connection_status() == CONNECTION_NORMAL)) { echo fread($fp, 8192); flush(); } fclose($fp);
X-Accel-Redirect 不支持断点续传,别误用
phpEnv 环境下有人试图用 X-Accel-Redirect 让 Nginx 代理下载,但它底层走的是 sendfile(),不解析 Range,也不返回 206,Nginx 会直接返回 200 全量内容,浏览器无法恢复。
-
X-Accel-Redirect只适合静态鉴权下载,不适合断点场景 - 若真要用 Nginx 原生断点,应把文件放在
location可直出路径下,并配置internal+alias,由 Nginx 自己处理 Range - 此时 PHP 只负责鉴权并跳转(
header('X-Accel-Redirect: /internal-download/xxx.zip');),但该路径必须由 Nginx 显式声明支持范围请求
最易被忽略的是 PHP 输出缓冲和 Nginx 的 proxy_buffering 双重叠加——两者都开着时,哪怕 Range 处理逻辑完全正确,浏览器收到的仍是完整响应体。调测时务必用 curl -v -H "Range: bytes=0-1023" http://yoursite/file.zip 直接验证响应状态码与头信息,而不是只看浏览器表现。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











