sendfile 与 tcp_nopush 必须成对启用且同时满足三重条件才生效:配置上均需显式开启;路径须为真实磁盘静态文件;响应头必须含 Content-Length,否则自动退回到 read/write 模式。
要让 sendfile 和 tcp_nopush 真正发挥零拷贝+极速传输效果,关键不是“开了就行”,而是确保它们在运行时协同生效。单独启用任一指令都无效,必须同时满足配置、路径、响应三重条件。
必须成对开启,且仅对静态文件生效
sendfile 负责跳过用户态内存拷贝,tcp_nopush 负责把响应头和文件开头数据合并进一个 TCP 包发出——但后者只有在前者走内核零拷贝路径时才被内核识别为“可攒包”的连续数据流。
配置上必须同时写:
sendfile on;
和
tcp_nopush on;
二者只对真实磁盘文件起作用:location 中需有
root
或
alias
指向本地路径,且匹配后缀如 .mp4、.zip、.jpg
proxy_pass、FastCGI、Lua 处理等动态路径不触发该机制,即使配置了也静默退回到 read/write 模式
三个硬性运行前提缺一不可
Nginx 在每次响应时动态判断是否启用零拷贝路径。以下三点必须全部满足,否则配置再正确也白搭:
当前 location 块中显式启用 sendfile on
(继承自上级块可能被覆盖,建议就近写)
请求命中的是可读的真实文件
(如 /data/videos/demo.mp4 存在,且未被 SELinux/AppArmor 拦截)
响应头含 Content-Length
(Nginx 需预知大小才能调用 sendfile();若后端返回 chunked 编码或无长度头,自动 fallback)
必须关闭干扰项,避免 silently disable
很多看似无关的配置会直接让 sendfile 自动禁用,进而导致 tcp_nopush 被忽略(Nginx 日志里会出现 “tcp_nopush is ignored”):
gzip on 必须关闭
:压缩必须在用户态完成,只要 gzip 启用(哪怕只针对 text/css),整个请求链路就退出零拷贝
禁用 sub_filter、access_by_lua_block、proxy_buffering on 等用户态处理模块
:它们强制数据经过用户空间
tcp_nodelay 必须为 off
:它与 tcp_nopush 逻辑冲突;设为 on 会强制立即发包,直接废掉“攒包”能力
推荐按资源类型精细化配置
不要在 http 块全局开启,应限定在明确的大静态资源 location 中:
对 HLS 场景,建议将 m3u8 单独剥离到不启用 tcp_nopush 的 location,避免首帧因凑包产生毫秒级延迟。
location ~ \.(mp4|zip|iso|tar\.gz|dmg|webp|avif)$ {
root /var/www/files;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
gzip off;
expires 7d;
}