tcp_nopush 对小文件有效需满足三个前提:启用 sendfile、响应含 Content-Length、禁用所有用户态处理模块;应限定于静态小资源路径,配合 open_file_cache 和 tcp_nodelay off 使用,以提升首包效率与吞吐。

tcp_nopush 本身不能单独压榨小文件带宽,它必须与 sendfile 协同,并且只在特定条件下对小文件有效——关键不是“小文件”,而是“能被内核一次性识别为连续数据块的静态响应”。对真正的小文件(如图标、1KB JS),优化重点其实是减少首包延迟和头部开销,而非单纯攒包。
必须满足的三个硬性前提
tcp_nopush 对小文件起效的前提比大文件更苛刻:
- sendfile on 必须启用:这是零拷贝路径的基础。Nginx 需跳过用户态缓冲,让内核直接从页缓存把文件内容推入 socket;否则 tcp_nopush 被忽略,日志中会出现 “tcp_nopush is ignored”
- 响应必须含明确的 Content-Length:小文件虽小,但 Nginx 仍需预知长度才能启用 sendfile。动态生成或 chunked 编码会强制退回到 read/write 模式
- 禁用所有用户态处理模块:gzip、sub_filter、ssi、access_by_lua_block 等任意一个开启,都会切断零拷贝链路,使 tcp_nopush 失效
精准作用于小文件资源类型
不要在 http 或 server 块全局开启 tcp_nopush。应限定在已知可缓存、无处理逻辑的小静态资源路径上:
- 匹配常见小资源后缀:
location ~* \.(ico|png|gif|jpg|webp|svg|js|css|woff2|ttf)$ - 搭配
open_file_cache使用:缓存文件元信息(inode、mtime),加快 open 系统调用,进一步缩短 sendfile 启动前的延迟 - 避免在含重写、鉴权或变量插值的 location 中启用:这些操作会触发用户态介入,绕过 sendfile
tcp_nodelay 必须设为 off
很多人误以为小文件该开 tcp_nodelay,但在 sendfile + tcp_nopush 场景中,这反而破坏优化:
- tcp_nodelay on 会强制内核立即发送未填满 MSS 的数据,导致首包只有 HTTP 头(约 200 字节)或极小文件体,白白浪费带宽利用率
- tcp_nopush on + tcp_nodelay off 才允许内核等待凑够约 1448 字节有效载荷(头 + 文件开头部分),让客户端第一包就拿到完整响应头+小文件全部内容
- 实测显示:对 2KB 以内的 PNG/JS/CSS,该组合可将首包 RTT 减少 1–3ms,吞吐提升 5–9%,因小包数量下降 60% 以上
配套缓存策略确保零拷贝通路畅通
缓存不等于零拷贝。要让小文件真正走 sendfile,需确保缓存内容能以文件描述符形式被内核访问:
- 本地磁盘缓存(
proxy_cache):必须配proxy_buffering off,否则 Nginx 先读进用户态 buffer,sendfile 失效 - 内存级缓存(
open_file_cache):仅缓存元数据,实际仍走 sendfile,对小资源最友好 - 对象存储直传(OSS/MinIO):上游响应必须带
Content-Length和200 OK,且 Nginx 配置proxy_buffering off;否则降级为 chunked,绕过优化










