sendfile 与 tcp_nopush 必须成对启用且配合 tcp_nodelay off,才能实现内核零拷贝与 tcp 包合并;仅对静态文件生效,需按 location 精准配置并避开 gzip、代理、无 content-length 等失效条件。

在高流量网站中,sendfile 不是“锦上添花”的可选项,而是降低 CPU 压力、提升静态资源吞吐的关键底层机制。它真正起效的前提不是简单写一句 sendfile on;,而是一整套协同配置与路径控制策略。
只对合适资源启用 sendfile
高流量站点请求类型混杂(HTML、JS、API、WebSocket、上传流),全局开启 sendfile 反而可能干扰非静态路径。应按 MIME 类型或后缀,在 location 级精准启用:
- 前端构建产物(如 CRA 的
/static/js/、/static/css/):匹配\.(js|css|svg|woff2?)$,启用sendfile on; tcp_nopush on;,并确保gzip off - 媒体与大文件(视频、安装包):单独设
location ~ \.(mp4|zip|dmg|iso)$,除 sendfile + tcp_nopush 外,建议加expires 7d;和limit_rate 10m;防带宽打满 - API 或动态接口(如
/api/)、WebSocket 路径必须排除 —— 这些场景不走文件读取,sendfile 自动失效,还可能因配置冲突引发日志警告
必须成对启用 tcp_nopush,且禁用 tcp_nodelay
tcp_nopush 不是独立优化项,它依赖 sendfile 提供的“内核连续数据流”才能触发 TCP_CORK 行为。二者不配对等于没开:
- 在相同 location 块中同时写
sendfile on;和tcp_nopush on; - 显式设置
tcp_nodelay off;(而非留空),避免从上级块继承意外开启;因为 tcp_nodelay 强制小包立即发出,直接抵消 tcp_nopush 的“攒包”效果 - HTTPS 下效果会打折扣(TLS 记录层拆包),但依然建议保留 —— 至少保证 HTTP/2 流中静态资源段受益
避开常见失效陷阱
即使配置正确,sendfile 在高并发下也容易静默退化。需主动规避以下典型冲突:
-
gzip 开启时 sendfile 自动关闭:React 构建后的 JS/CSS 通常已压缩,Nginx 层无需再 gzip;若必须压缩(如未预压缩的 HTML),应将压缩逻辑前置(如用
gzip_static on;服务预压缩文件),而非运行时 gzip -
proxy_buffering off 或无 Content-Length 时失效:反向代理大文件(如直连对象存储)时,务必让后端返回明确
Content-Length,并设proxy_buffering off;,否则 Nginx 无法确认文件边界,回退到 read/write 模式 -
sendfile_max_chunk 设得太小:默认为 0(不限),若设为
512k以上,需确认内核支持(≥3.10);超限文件会分块降级,对首屏 JS 等关键小文件影响不大,但对单个 10MB 视频会造成多次系统调用
验证是否真正在跑零拷贝
上线后不能只看配置,要用实际信号确认生效:
- 用
strace -e trace=sendfile64 -p $(pgrep -f "nginx: worker")抓 worker 进程,请求一个 JS 文件,观察是否出现sendfile64(…)系统调用 - 对比压测前后 CPU 使用率(特别是
%sys时间):高并发下,启用后 sys 时间应明显下降(降幅常达 15–30%) - 检查响应头:虽然无标准字段,但若看到
X-Accel-Buffering: no(部分发行版添加)或响应中无Transfer-Encoding: chunked,且有明确Content-Length,是良好信号











