sendfile 与 tcp_nopush 必须成对启用且满足静态文件、Content-Length、禁用 gzip 等条件才协同生效,实现零拷贝与 TCP 包合并,提升首包响应与吞吐;单独开启任一指令无效。

sendfile 和 tcp_nopush 不是“开了就快”的开关,而是必须成对启用、严格满足条件才能生效的一组内核级协同机制。它们真正起效时,能跳过用户态内存拷贝、减少系统调用、合并 TCP 报文,显著提升图片、视频、安装包等静态资源的首包响应速度和吞吐效率。
为什么必须一起开?
tcp_nopush 的作用不是延迟发包,而是让内核把 HTTP 响应头和文件开头数据“攒进同一个 TCP 报文”发出——但这个“攒”动作的前提是:数据必须已准备好、且在内核中呈现为逻辑连续的一整块。
只有 sendfile 启用后,Nginx 才会走零拷贝路径:内核直接从磁盘 fd 复制到 socket fd,绕过用户态缓冲区。此时响应头写入后,文件起始内容可被内核视为紧邻数据流,tcp_nopush 才真正触发合并。
若只开 tcp_nopush 而 sendfile 关闭(比如启用了 gzip、proxy_buffering 或后端没返回 Content-Length),Nginx 日志会明确提示 “tcp_nopush is ignored”,该配置实际失效。
哪些场景下它们才真正工作?
这两个指令仅在满足全部以下条件时激活:
- 配置了 sendfile on,且位于匹配真实静态文件的 location 中(如 root 或 alias 指向本地路径)
- 请求的是明确后缀的静态资源(如 .jpg、.mp4、.zip),而非 proxy_pass、FastCGI 或动态生成内容
- 响应头中包含准确的 Content-Length(Nginx 需预知大小才能启用 sendfile 路径)
- 该 location 下未启用 gzip、sub_filter 等需用户态处理的模块
怎么配才不踩坑?
全局开启容易误伤 API、WebSocket 等低延迟服务。推荐按资源类型做 location 级精准控制:
- 图片类:location ~* \.(jpg|jpeg|png|webp|gif|svg|avif)$ { sendfile on; tcp_nopush on; gzip off; }
- 大文件类:location ~ \.(mp4|zip|iso|tar\.gz|dmg)$ { sendfile on; tcp_nopush on; tcp_nodelay off; gzip off; }
- 务必显式关闭 gzip —— 压缩会强制走用户态流程,直接导致 sendfile 自动禁用
- 若代理对象存储等后端,需设 proxy_buffering off,并确保上游返回 Content-Length
配套参数不能漏
tcp_nopush 不是孤立生效的,它与几个关键参数强耦合:
- tcp_nodelay 必须为 off:两者互斥。on 会强制立即发包,完全抵消“攒包”效果
- client_header_buffer_size 和 client_body_buffer_size 建议设为 16k,避免因 Cookie 或 SNI 过长触发 400 错误,间接阻断静态请求
- 大文件建议加 sendfile_max_chunk 512k,防止单次 sendfile 调用阻塞事件循环

















