必须成对启用sendfile和tcp_nopush,并在静态文件location块中同步配置tcp_nodelay off,否则tcp_nopush无效;其作用依赖内核零拷贝路径,受gzip、proxy_pass、缺失content-length等因素影响而退化。

要真正减少小报文和网络分片,sendfile 和 tcp_nopush 必须成对启用,并且只在满足特定条件的静态文件路径下才起作用——不是加了就生效,而是得让 Nginx 走通内核零拷贝路径。
为什么单开 tcp_nopush 没用
tcp_nopush 本质是启用 Linux 的 TCP_CORK 机制,它不会立即发包,而是等“响应头 + 文件数据”能凑满一个 TCP 报文段,或等到 sendfile 完成再统一推送。但它完全依赖 sendfile:只有 sendfile on 开启后,Nginx 才把文件控制权交给内核;否则数据必须经用户态缓冲、拼接、再 write() 发送,此时 tcp_nopush 直接被忽略,日志里会出现 “tcp_nopush is ignored”。
- sendfile 关闭 → tcp_nopush 形同虚设
- 用了 proxy_pass、gzip、add_header、sub_filter → 自动退化为 read/write 模式 → tcp_nopush 不参与
- 响应头不含 Content-Length(比如 chunked 编码)→ 内核无法预估长度 → sendfile 调用失败 → tcp_nopush 失效
必须同步配置的三项基础指令
这三者缺一不可,且必须写在具体服务静态文件的 location 块里,不能只靠 http 或 server 块继承:
- sendfile on; —— 启用内核零拷贝,跳过用户态内存拷贝
- tcp_nopush on; —— 允许内核暂存响应头与文件开头数据,合并进首个 TCP 报文段
- tcp_nodelay off; —— 显式关闭 Nagle 算法,避免它抢在 tcp_nopush 之前强行发小包(二者在内核中互斥)
配套参数决定实际效果
光有三件套还不够,缓冲空间和文件边界也影响是否真能“攒满一包”:
- output_buffers 2 128k; —— 默认 2×32k 容易在大文件(如高清图、短视频首帧)传输中提前冲刷,调大可提升合并概率
- sendfile_max_chunk 512k; —— 防止单次 sendfile 过大阻塞事件循环,也适配常见大静态资源的首段体积
- gzip off; —— 压缩必须走用户态,一开就禁用 sendfile
- expires 或 add_header 不改写 Content-Length; —— 添加额外 header 可能导致长度不匹配,触发退化
按文件类型精细化启用最稳妥
不要在全局或 server 块里开 tcp_nopush。应针对明确的大体积静态资源后缀,在独立 location 中配置:
- 视频类:
.mp4 .webm .mkv - 安装包类:
.zip .dmg .iso .tar.gz - 高质图像类:
.webp .tiff .psd - 字体与长 JS/CSS(>500KB)也可考虑,但需验证 Content-Length 稳定性
示例配置:
location ~ \.(mp4|zip|webp|iso)$ {root /data/assets;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
gzip off;
output_buffers 2 256k;
sendfile_max_chunk 512k;
}
不复杂但容易忽略











