tcp_nopush 与 sendfile 必须协同启用且满足 sendfile on、服务真实静态文件、响应含 Content-Length 三前提,才能实现零拷贝与 TCP 报文合并;仅在 location 级按文件类型精细化配置,并禁用 gzip,配合 tcp_nodelay off 才可生效。

tcp_nopush 与 sendfile 是一对协同工作的内核级优化指令,专为提升大文件(如视频、安装包)的静态分发效率而设计。单独开启任一指令效果有限,只有两者配合且配置得当,才能真正减少网络小包、降低 CPU 拷贝开销、加快首屏响应。
必须满足的前提条件
这两个指令不是“开了就见效”,需同时满足以下三点:
- sendfile on 必须启用:这是零拷贝的基础。Nginx 由此跳过用户态缓冲,直接由内核将磁盘文件数据送入 socket 发送队列
- 服务内容必须是真实磁盘静态文件:例如 location 中 root 或 alias 指向本地路径,且请求路径能明确匹配到 .mp4、.zip 等后缀;proxy_pass、FastCGI、gzip 压缩响应等场景均不触发该路径
- 响应头中需含 Content-Length:Nginx 需预知文件大小,才能决定是否启用 sendfile 路径;若后端动态生成或流式响应无长度头,会自动回退到常规 read/write 流程
tcp_nopush 的实际作用机制
它并非“延迟发送”,而是让内核把 HTTP 响应头和文件开头的数据尽可能合并进同一个 TCP 报文段发出——这依赖于 sendfile 提供的连续数据流。关键点在于:
- 仅在 sendfile 生效时起作用,否则配置了也无效
- 需搭配 tcp_nodelay off(默认即为 off):允许内核等待凑够 MSS(通常约 1448 字节有效载荷),从而让 tcp_nopush 有机会合并;若设为 on,会强制立即发包,抵消其效果
- 对首屏加载帮助明显:客户端收到第一个 TCP 包就同时拿到状态行、响应头和视频前几 KB 数据,无需再等一次 RTT 才开始解码
推荐的精细化配置方式
不要在 http 或 server 块全局开启,应按资源类型限定在具体 location 中:
location ~ \.(mp4|webm|avi|mov|zip|iso|tar\.gz)$ {
root /data/assets;
sendfile on;
tcp_nopush on;
tcp_nodelay off; # 显式声明,避免被继承值干扰
expires 7d;
add_header Cache-Control "public, immutable";
# 关闭 gzip 防冲突(若未关,sendfile 会自动禁用)
gzip off;
}注意:若该 location 下还需支持 range 请求(如视频拖拽),确保启用了 underscores_in_headers on 和正确处理 If-Range 头,但 sendfile + range 在多数 Linux 内核下仍可正常工作。
如何确认是否真正生效
不能只看配置语法正确,要验证运行时行为:
- 用
strace -p $(pgrep nginx) -e trace=sendfile64观察 worker 进程是否调用系统调用 - 抓包分析:用
tcpdump或 Wireshark 查看响应流量,开启后应观察到更多“HTTP/1.1 200 OK”与文件数据同在一个 TCP 段中 - 检查连接状态:
ss -i 'dst :80'输出中若出现np标记(表示 TCP_CORK 已启用),说明 tcp_nopush 正在作用


















