HTTP断点续传依赖两个核心要素:客户端发起带Range请求头的GET请求,服务器识别该请求并返回206 Partial Content状态码及对应的Content-Range响应头。而服务器是否愿意处理Range请求,通常会通过Accept-Ranges头部提前告知客户端。Apache对静态文件的默认处理会自动产生Accept-Ranges: bytes,这使得浏览器、下载工具以及curl等客户端可以放心使用范围请求。下面先展示响应头中的实际效果。

Accept-Ranges头部的取值只有两种常见形式:bytes表示服务器支持以字节为单位进行范围请求;none则表示不支持任何范围请求。如果响应中没有这个头部,客户端通常会假定服务器不支持断点续传,但一些激进的下载器仍会尝试发送Range头进行探测。Apache处理静态文件时,该头部由核心模块自动添加,无需额外配置。不过当反向代理、自定义脚本或模块干预响应时,头部可能会丢失或被覆盖,这时就需要手动配置。
Apache静态文件的默认Accept-Ranges行为
Apache对磁盘上真实存在的静态文件(如HTML、图片、压缩包、视频等)提供服务时,核心代码会检查请求方法是否允许范围处理,若允许则在响应头中写入Accept-Ranges: bytes。这个行为与是否启用了mod_headers、mod_include等模块无关,是HTTP协议栈层面的默认实现。可以用一个简单的实验来观察:在Apache文档根目录放置一个名为test.bin的文件,大小超过1KB即可,然后执行curl命令。
curl -I http://127.0.0.1/test.bin
返回结果中可以看到类似下面的头部信息,其中Accept-Ranges明确存在,同时带有Last-Modified和ETag等用于缓存校验的字段。这些字段与Range请求共同作用,保证断点续传下载的内容与完整文件一致。
HTTP/1.1 200 OK Date: Wed, 08 Jan 2025 10:00:00 GMT Server: Apache/2.4.58 (Unix) Last-Modified: Tue, 07 Jan 2025 16:00:00 GMT ETag: "1a2b3c-5d6e-6123456789abc" Accept-Ranges: bytes Content-Length: 204800 Content-Type: application/octet-stream
如果此时向服务器发送带Range头的GET请求,例如只请求前1024字节,Apache会返回206状态码,并添加Content-Range: bytes 0-1023/204800这样的响应头。注意Range的字节区间是闭区间,结束位置等于起始位置加长度减一。这个细节在手动构造请求时经常被忽略,导致客户端解析错误。
对于默认支持范围请求的静态文件,Apache还会正确处理多段Range请求,例如Range: bytes=0-99,200-299。此时响应中的Content-Type会变为multipart/byteranges,每一段数据用独立的边界分隔。不过这会增加服务器处理开销,很多下载工具在普通断点续传场景下只使用单段Range。
通过Header指令手动控制Accept-Ranges
某些情况下需要主动添加或移除Accept-Ranges头部。比如使用Apache作为反向代理,后端服务器不支持范围请求,但希望客户端仍然尝试断点续传,可以强制添加该头部。反之,如果希望彻底禁用范围请求(例如保护敏感文件或解决某些客户端兼容性问题),可以移除或设置为none。Apache的mod_headers模块提供了灵活的Header指令,可以在服务器配置、虚拟主机或.htaccess文件中使用。
下面的配置示例在虚拟主机级别强制所有响应都带上Accept-Ranges: bytes,即使后端脚本本身没有输出该头部。如果已经存在同名头部,使用set会覆盖原值,使用add会追加一个同名头部,而append则会合并值。此处推荐使用set确保只有一个明确的值。
<VirtualHost *:80>
ServerName download.ipipp.com
DocumentRoot /var/www/download
<Directory /var/www/download>
Options -Indexes
AllowOverride All
Require all granted
</Directory>
# 强制所有响应包含 Accept-Ranges: bytes
Header set Accept-Ranges "bytes"
</VirtualHost>
需要注意的是,仅仅添加Accept-Ranges头部并不代表服务器真的支持范围请求。如果后端应用没有处理Range头,Apache只是把原样响应发给客户端,客户端发起Range请求后得到的仍然是200 OK和完整文件,断点续传实际上并没有生效。因此在反向代理场景下,需要后端同时正确解析Range请求并返回206状态码。对于Apache直接处理的静态文件,控制器不会产生这种不一致。
若需要禁用范围请求,可以写成Header unset Accept-Ranges,或者设置为none。某些安全扫描工具会建议不要暴露文件大小等信息,但禁用Accept-Ranges并不能隐藏Content-Length,而且会直接影响大文件下载体验,实际生产环境中很少这样做。
Accept-Ranges与缓存校验字段的配合
断点续传并不是孤立工作的,客户端在发起Range请求后,服务器返回的206响应中必须包含ETag或Last-Modified,以便客户端后续请求时进行条件校验。例如下载暂停后文件发生变化,客户端重新请求时带上If-Range头,服务器通过ETag或时间戳判断文件未变才返回206,否则返回完整的200响应。Apache静态文件默认同时生成ETag和Last-Modified,并且If-Range优先匹配ETag,这也符合RFC 7233的建议。
# 模拟断点续传:请求从第1024字节开始,且带If-Range校验
curl -H "Range: bytes=1024-" -H "If-Range: \"1a2b3c-5d6e-6123456789abc\"" \
-o resumed-part.bin http://127.0.0.1/test.bin
如果服务器返回206,表示文件未变化,客户端可以把之前下载的部分与本次获取的部分拼接起来。如果返回200,说明文件已更新,客户端必须丢弃旧数据重新开始下载。Apache在处理If-Range时,会先检查ETag是否匹配,若匹配则继续处理Range;若不匹配则忽略Range头,返回完整的200响应。这种机制保证了断点续传的数据完整性。
对于动态生成的资源,例如PHP输出的下载流,默认情况下PHP不会自动设置Accept-Ranges,也不会处理Range头。在Apache动态内容响应中,Accept-Ranges通常缺失或者为none。如果希望PHP实现断点续传,需要在脚本中手动解析HTTP_RANGE环境变量,并输出206状态码及Content-Range头,同时保证Accept-Ranges头部存在。Apache的mod_php只是把请求转给PHP解释器,不会替应用处理Range逻辑。
排查Accept-Ranges头部缺失的常见原因
当curl -I命令看不到Accept-Ranges: bytes时,先确认请求的URL是否真的对应静态文件。Apache对目录请求、错误页面、自动生成的索引页等可能不会附带Accept-Ranges。另外,如果启用了mod_deflate等输出过滤器进行压缩,范围请求会变得复杂,部分Apache版本在压缩响应中会移除Accept-Ranges,因为压缩后的字节范围无法直接对应原始文件。此时可以关闭特定文件类型的压缩来解决。
反向代理配置错误也是一个高发问题。例如使用ProxyPass将所有请求转发给后端,而Apache自身并没有缓存或处理静态文件,Accept-Ranges应由后端服务器提供。如果后端是Nginx或Tomcat且配置正确,Apache会把响应头原样透传。若后端没有输出该头部,可以在Apache的代理配置中使用Header add Accept-Ranges "bytes"来补充,但必须确保后端确实能处理Range。
最后要关注.htaccess中的Header指令是否被覆盖。如果在Directory上下文中使用了Header unset Accept-Ranges,而其他配置文件又尝试添加,最终结果取决于模块处理顺序。调试时可以使用curl -v查看完整响应头,结合apachectl -t检查配置语法,并观察error_log中的相关记录。通过上述排查步骤,一般都能定位到Accept-Ranges头部缺失或异常的具体原因。
ApacheAccept-Ranges断点续传修改时间:2026-09-25 11:57:01