tcp_nopush在Linux 4.x中仅与sendfile on配合且满足Content-Length、无用户态处理等条件时,通过启用TCP_CORK优化静态文件传输;否则无效。

tcp_nopush 在 Linux 4.x 上不会单独生效,必须和 sendfile on 严格配对,且只在满足一连串硬性条件的静态文件响应中起作用。
tcp_nopush 实际启用的是 TCP_CORK,不是“延迟发送”
Linux 4.x(如 4.4、4.15、4.19)中,tcp_nopush on 本质是设置 TCP_CORK 选项。它的行为是:当响应头已写入 socket,且后续待发数据(比如文件开头几百字节)能接近 MSS(通常 1448–1460 字节)时,内核暂存这些数据,等凑满一包再发出——前提是数据走的是 sendfile 路径。
这不是人为加 delay,也不是 Nagle 算法;它依赖内核零拷贝路径,一旦数据被拉进用户态(比如 gzip 压缩),tcp_nopush 就彻底失效。
- 若
sendfile off,Nginx 必须先read()到用户空间再write(),tcp_nopush被忽略(日志会警告 “tcp_nopush is ignored”) - 只要启用了
gzip on、sub_filter、access_by_lua_block等任意用户态处理模块,sendfile自动禁用,tcp_nopush同步归零 - 响应必须含明确的
Content-Length;否则 Nginx 退化为 chunked 编码,无法预分配长度,sendfile不启动
只在特定 location 中启用,别全局开
全局配置 tcp_nopush on 是低效且危险的操作。它只对真正走 sendfile 的响应有效,而绝大多数动态请求、API、带重写的路径根本不会触发。
应按资源类型精准控制,例如:
location ~ \.(mp4|zip|iso|tar\.gz|dmg)$ {
root /var/www/files;
sendfile on;
tcp_nopush on;
gzip off;
add_header Content-Transfer-Encoding binary;
}
- 避免在
/api/、/admin/、含rewrite或proxy_pass的 location 中启用 - HLS 场景下,
m3u8文件建议单独剥离到不启用tcp_nopush的 location,否则首屏可能轻微卡顿 - 若需兼容某些带 ETag 或缓存头的静态资源,放心——仅操作 header 不影响
sendfile,只要没启用压缩或过滤器即可
验证是否真生效,不能只看配置有没有写
配置写了不等于运行时起作用。Linux 4.x 下必须交叉验证:
- 用
ss -i dst查连接,输出中出现np标记(表示 TCP_CORK 已启用) - 用
strace -e trace=sendfile,read,write -p $(pgrep nginx)观察 worker 进程:看到大量sendfile调用,且几乎无read+write组合,说明路径正确 - Wireshark 抓包对比:开启后,HTTP 响应头与文件前段数据应合并进 1–2 个 TCP 段,而不是拆成多个小包(如多个 40–60 字节的 ACK + 数据)
最容易被忽略的一点:即使你把 sendfile 和 tcp_nopush 都开了,只要后端返回的响应里缺 Content-Length(比如代理对象存储但未透传该头),或者 Nginx 自己加了 gzip,整个链路就掉回用户态 read/write —— 此时 tcp_nopush 形同虚设,你还以为调优成功了。











