Nginx 反向代理性能调优:从入门到生产
Nginx 作为反向代理几乎是现代 Web 架构的标配。但很多人止步于 proxy_pass 的基本配置,默认参数跑起来能工作,一上量就出问题——502 频发、SSL 握手拖慢首字节、缓存没命中反而多了一跳、限流太粗暴误杀正常用户。
本文从反向代理基础概念出发,逐层深入到 worker 调优、负载均衡、SSL 加速、缓存策略、压缩传输、限流安全和监控体系,最后给出一份可直接用于生产环境的检查清单。
一、反向代理基础概念
正向代理 vs 反向代理
很多人分不清正向代理和反向代理,核心区别在于「代理谁」:
| 特性 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端 | 服务端 |
| 谁知道代理的存在 | 客户端知道 | 客户端不知道 |
| 典型场景 | VPN、科学上网、企业出口代理 | 负载均衡、SSL 终结、缓存加速 |
| 控制权 | 客户端选择是否走代理 | 服务端决定代理规则 |
典型架构
┌──────────────┐
│ 客户端浏览器 │
└──────┬───────┘
│ HTTPS
┌──────▼───────┐
│ Nginx 反向代理 │ ← SSL 终结 + 缓存 + 限流
└──────┬───────┘
┌────────┼────────┐
┌──────▼──┐ ┌───▼────┐ ┌─▼────────┐
│ 后端服务 A │ │ 后端服务 B │ │ 静态资源 CDN │
│ :3000 │ │ :8080 │ │ S3/OSS │
└─────────┘ └────────┘ └──────────┘
Nginx 在这个架构中承担了 SSL 终结、请求分发、缓存、压缩、限流等多重职责,每一层都有调优空间。
最简反向代理配置
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}
这段配置能跑,但远不够生产级别。接下来的章节会逐步补全它。
二、核心配置参数详解
1. worker_processes:进程数
Nginx 采用多进程架构,每个 worker 进程是单线程事件循环。worker_processes 决定了有多少个进程来处理请求。
# nginx.conf 顶层
# 方式1:手动指定(推荐生产环境)
worker_processes 4;
# 方式2:自动匹配 CPU 核心数
worker_processes auto;
auto 是最安全的选择,Nginx 会自动检测 CPU 核心数。手动指定适用于容器化环境,避免 auto 读取到宿主机的核心数(如 Kubernetes 中 auto 可能读到 64 核而容器只限了 2 核)。
2. worker_connections:单进程连接数
events {
# 单个 worker 最大并发连接数
worker_connections 4096;
# 允许一个 worker 同时接受多个新连接
multi_accept on;
# Linux 推荐使用 epoll
use epoll;
}
理论最大并发 = worker_processes × worker_connections。4 worker × 4096 connections = 16384 并发。但实际中还要扣除与上游的连接,所以有效并发大约是计算值的 60%~70%。
worker_connections 受 ulimit -n(文件描述符上限)约束。如果 ulimit -n 是 1024,设 worker_connections 4096 也只能用到 1024。生产环境建议 ulimit -n 65535 或在 systemd service 中配置 LimitNOFILE=65535。
3. keepalive:连接复用
http {
# 客户端 → Nginx 的 keepalive
keepalive_timeout 65; # 超时时间(秒),0 则禁用
keepalive_requests 1000; # 单个连接最大请求数
# Nginx → 上游的 keepalive(关键!)
upstream backend {
server 127.0.0.1:3000;
keepalive 32; # 到上游的空闲 keepalive 连接池大小
}
}
upstream.keepalive 是容易被忽略但极其重要的参数。没有它,每次请求都要新建 TCP 连接到上游,握手开销在短连接场景下可能占到响应时间的 30%+。
# 必须配合以下头部,否则 keepalive 不生效
location / {
proxy_pass http://backend;
proxy_http_version 1.1; # 默认 1.0,不支持 keepalive
proxy_set_header Connection ""; # 清除 Connection: close
proxy_set_header Host $host;
}
4. buffer 系列:请求/响应缓冲
http {
# 响应缓冲:Nginx 先读完上游响应,再发给客户端
proxy_buffer_size 4k; # 响应头缓冲(通常 4k 足够)
proxy_buffers 8 16k; # 响应体缓冲(8 块 × 16k = 128k)
proxy_busy_buffers_size 32k; # 忙时缓冲上限
# 请求体缓冲:客户端请求体太大时先写磁盘
client_max_body_size 20m; # 最大请求体(超过返回 413)
client_body_buffer_size 128k; # 内存缓冲区大小
client_body_temp_path /tmp/nginx-body 1 2; # 磁盘临时路径
# 大响应场景:禁用缓冲,流式转发
# proxy_buffering off; # 适用于 SSE/WebSocket/大文件下载
}
三、负载均衡策略
五种策略对比
| 策略 | 指令 | 特点 | 适用场景 |
|---|---|---|---|
| 轮询(默认) | (默认,无需指定) | 均匀分配,不考虑服务器差异 | 后端机器配置相同 |
| 加权轮询 | weight=N |
按权重分配,高配机器多分 | 后端机器配置不同 |
| IP 哈希 | ip_hash |
同一 IP 固定到同一后端 | 需要会话保持(无状态更好) |
| 最少连接 | least_conn |
优先分配给当前连接数最少的服务器 | 请求处理时间差异大 |
| 一致性哈希 | hash $key consistent |
一致性环,增减节点影响小 | 缓存命中率敏感场景 |
配置示例
# 加权轮询
upstream weighted {
server 10.0.0.1:3000 weight=5; # 5/8 的流量
server 10.0.0.2:3000 weight=2; # 2/8 的流量
server 10.0.0.3:3000 weight=1; # 1/8 的流量
}
# IP 哈希(⚠️ 尽量用无状态替代)
upstream iphash {
ip_hash;
server 10.0.0.1:3000;
server 10.0.0.2:3000;
}
# 最少连接
upstream leastconn {
least_conn;
server 10.0.0.1:3000;
server 10.0.0.2:3000;
}
# 一致性哈希
upstream consistent {
hash $request_uri consistent; # 按 URI 做一致性哈希
server 10.0.0.1:3000;
server 10.0.0.2:3000;
server 10.0.0.3:3000;
}
健康检查
upstream backend {
server 10.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 10.0.0.2:3000 max_fails=3 fail_timeout=30s;
# Nginx Plus(商业版)才支持主动健康检查
# health_check interval=5s fails=3 passes=2;
}
# 开源版替代方案:间隔性请求
# 用 cron + curl 或第三方模块(nginx_upstream_check_module)
hash $cookie_sessionid consistent 替代。
四、SSL/TLS 性能优化
SSL 握手是 HTTPS 延迟的主要来源。一次完整的 TLS 1.2 握手需要 2 个 RTT,TLS 1.3 缩短到 1 个 RTT,但仍可通过缓存和优化进一步降低。
1. Session Cache:复用握手结果
server {
listen 443 ssl http2;
server_name example.com;
# Session Cache:共享内存缓存,worker 间共享
ssl_session_cache shared:SSL:10m; # 10MB ≈ 40000 个 session
ssl_session_timeout 1d; # session 有效期 1 天
ssl_session_tickets on; # Session Ticket(无状态,推荐)
# 证书配置
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
}
shared:SSL:10m 表示所有 worker 共享一块 10MB 的内存,1MB 大约存 4000 个 session,10MB ≈ 40000 个。根据并发量调整。
2. OCSP Stapling:减少证书验证延迟
server {
# OCSP Stapling:Nginx 主动获取 OCSP 响应,附在握手时发给客户端
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem; # 完整证书链
resolver 8.8.8.8 8.8.4.4 valid=300s; # DNS 解析器
resolver_timeout 5s;
}
没有 OCSP Stapling 时,客户端需要单独访问 CA 的 OCSP 服务器验证证书状态,额外增加一个 RTT + DNS 解析。开启后 Nginx 主动缓存 OCSP 响应,客户端无需额外请求。
3. 协议与密码套件选择
server {
# 协议:只开 TLS 1.2 和 1.3
ssl_protocols TLSv1.2 TLSv1.3;
# 密码套件:优先 ECDHE(前向保密)+ AEAD(性能好)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
ssl_prefer_server_ciphers on;
# 证书链优化:减少中间证书层级
# 合并证书链:cat cert.pem intermediate.pem > fullchain.pem
}
4. HTTP/2 与 HTTP/3
server {
# HTTP/2(基于 TLS)
listen 443 ssl http2;
# HTTP/3(QUIC,Nginx 1.25+)
listen 443 quic reuseport;
# Alt-Svc 头,告诉客户端支持 H3
add_header Alt-Svc 'h3=":443"; ma=86400';
}
# UDP 443 端口也要放行(QUIC 基于 UDP)
# 防火墙:iptables -A INPUT -p udp --dport 443 -j ACCEPT
HTTP/2 的多路复用消除了队头阻塞(HTTP 层面),HTTP/3(QUIC)进一步解决了 TCP 层面的队头阻塞,且 0-RTT 握手让重连几乎零延迟。
五、缓存策略
1. proxy_cache 基础层级
http {
# 缓存路径配置
proxy_cache_path /var/cache/nginx
levels=1:2 # 两级子目录,避免单目录文件过多
keys_zone=api_cache:10m # 共享内存区域,存缓存键
max_size=10g # 磁盘最大占用
inactive=60m # 60 分钟未被访问则淘汰
use_temp_path=off; # 临时文件直接写在目标目录,减少跨磁盘拷贝
}
2. 缓存键与命中率
server {
location /api/ {
proxy_pass http://backend;
proxy_cache api_cache;
# 缓存键:默认 $scheme$proxy_host$request_uri
# 自定义缓存键(区分登录态、分页等)
proxy_cache_key "$scheme$request_method$host$request_uri";
# 缓存有效期
proxy_cache_valid 200 302 10m; # 200/302 响应缓存 10 分钟
proxy_cache_valid 404 1m; # 404 缓存 1 分钟
proxy_cache_valid any 5s; # 其他响应缓存 5 秒
# 命中状态头(调试用)
add_header X-Cache-Status $upstream_cache_status;
# 忽略上游的 Cache-Control(慎用)
# proxy_ignore_headers Cache-Control Set-Cookie;
}
}
3. 缓存清除
# 方式1:基于 URL 的缓存清除(需要 proxy_cache_purge 模块)
location /api/ {
proxy_cache api_cache;
proxy_cache_purge PURGE from 127.0.0.1; # 只允许本机发起清除
}
# 方式2:手动删除缓存文件
# find /var/cache/nginx -type f -delete
# 然后 reload:nginx -s reload
# 方式3:修改缓存键强制失效(最安全)
# 在 proxy_cache_key 中加入版本号
proxy_cache_key "$scheme$request_method$host$request_uri$v1";
# 更新版本号 → 所有旧缓存自然失效
4. stale-while-revalidate:过期间隙服务
location /api/ {
proxy_cache api_cache;
# 缓存过期后,先返回旧缓存,后台异步刷新
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
# 正在刷新时,允许返回过期缓存
proxy_cache_lock on; # 同一资源只有一个请求去上游取
proxy_cache_lock_timeout 5s; # 等待锁超时
# 后台更新(Nginx 1.11+)
proxy_cache_background_update on;
}
proxy_cache_lock 确保只有一个请求去上游取新数据,其余请求返回旧缓存(即使过期),避免了「缓存击穿」风暴。
六、压缩与传输优化
1. Gzip / Brotli 压缩
http {
# Gzip(内置)
gzip on;
gzip_min_length 1024; # 小于 1KB 不压缩(压缩比低,还浪费 CPU)
gzip_comp_level 4; # 压缩级别 1-9,4 是性能与压缩率的最佳平衡
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml; # SVG 文本格式,压缩效果好
gzip_vary on; # 添加 Vary: Accept-Encoding 头
# Brotli(需要模块:ngx_brotli)
# 压缩率比 gzip 高 15-25%,现代浏览器都支持
brotli on;
brotli_comp_level 4;
brotli_types text/plain text/css application/javascript application/json;
}
gzip_comp_level 9 比 4 压缩率只多 5% 左右,但 CPU 消耗翻 3 倍。对于动态内容,高压缩级别反而增加 TTFB。生产环境推荐 4-6。
2. TCP 优化
http {
# 操作系统级 TCP 优化
sendfile on; # 零拷贝,内核直接将文件发到 socket
tcp_nopush on; # 配合 sendfile,攒够一个完整包再发
tcp_nodelay on; # 禁用 Nagle 算法,小包立即发送
# TCP 快速打开(Linux 3.7+)
# 客户端可以在 SYN 包中携带数据,减少 1 个 RTT
listen 80 fastopen=256; # 256 是队列长度
}
sendfile + tcp_nopush + tcp_nodelay 三件套是 Nginx 性能的基石:sendfile 跳过用户态拷贝,tcp_nopush 让大响应攒够包再发减少系统调用,tcp_nodelay 确保最后一个不满的包立即发出不延迟。
3. 静态资源优化
server {
location /static/ {
alias /var/www/static/;
# 长期缓存(文件名含 hash 时)
expires 365d;
add_header Cache-Control "public, immutable";
# 开启 sendfile
sendfile on;
tcp_nopush on;
}
location /assets/ {
alias /var/www/assets/;
expires 30d;
add_header Cache-Control "public";
}
}
七、限流与安全
1. limit_req:请求速率限制
http {
# 定义限流区域(基于客户端 IP)
# rate=10r/s 表示每秒最多 10 个请求
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
# burst=20:允许突发 20 个请求排队
# nodelay:突发请求不延迟,超出直接拒绝
limit_req zone=api_limit burst=20 nodelay;
# 被限流时返回 429 而非默认的 503
limit_req_status 429;
}
}
}
burst=20 允许短时间内 20 个额外请求排队;nodelay 让排队的请求立即处理而不是匀速放行。超出 burst 的请求直接 429。这是最常用的限流模式——既容忍突发,又保护后端。
2. limit_conn:并发连接限制
http {
# 基于客户端 IP 的并发连接数限制
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
# 单个 IP 最多 50 个并发连接
limit_conn zone=conn_limit 50;
# 被限制时返回 503
limit_conn_status 503;
}
}
3. 连接超时防护
server {
# 客户端超时
client_header_timeout 10s; # 读取请求头超时
client_body_timeout 10s; # 读取请求体超时
send_timeout 10s; # 响应发送超时
# 上游超时
proxy_connect_timeout 5s; # 与上游建立连接超时
proxy_send_timeout 10s; # 向上游发送请求超时
proxy_read_timeout 30s; # 等待上游响应超时
# 慢速攻击防护
# 如果客户端每秒发送速率低于 500 字节,10 秒后断开
client_body_timeout 10s;
lingering_timeout 5s;
}
4. WAF 基础防护
server {
# 隐藏版本号
server_tokens off;
# 基础安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# HSTS(强制 HTTPS,谨慎开启,max-age 先设小值测试)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# CSP(内容安全策略,按需配置)
# add_header Content-Security-Policy "default-src 'self'" always;
# 拒绝非法 User-Agent
if ($http_user_agent ~* (sqlmap|nikto|nmap|masscan)) {
return 403;
}
# 拒绝非法请求方法
if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS|PATCH)$) {
return 405;
}
# 阻止常见注入路径
location ~* /\.\.(/|$) {
return 403;
}
}
八、日志与监控
1. 自定义日志格式
http {
# 自定义日志格式:包含响应时间、上游响应时间、缓存状态
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time ' # Nginx 处理总时间
'urt=$upstream_response_time ' # 上游响应时间
'cs=$upstream_cache_status ' # 缓存状态
'ccs=$sent_http_content_type'; # 响应 Content-Type
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
}
2. 条件日志:减少磁盘写入
http {
# 只记录非 2xx/3xx 的请求(减少健康检查的日志噪音)
map $status $loggable {
~^[2-3] 0; # 2xx、3xx 不记录
default 1; # 其他记录
}
access_log /var/log/nginx/access.log main if=$loggable;
}
3. 实时监控:stub_status
server {
listen 127.0.0.1:8080; # 只监听本地
location /nginx_status {
stub_status; # 暴露基本状态
access_log off; # 不记录此路径的日志
# 输出示例:
# Active connections: 291
# server accepts handled requests
# 16630948 16630948 31070465
# Reading: 6 Writing: 179 Waiting: 106
}
}
4. Prometheus 集成
# 方式1:nginx-prometheus-exporter(官方)
# Docker 部署
docker run -d -p 9113:9113 \
nginx/nginx-prometheus-exporter:latest \
--nginx.scrape-uri=http://nginx:8080/nginx_status
# Prometheus 配置
scrape_configs:
- job_name: 'nginx'
static_configs:
- targets: ['exporter:9113']
# 方式2:lua-nginx-module + nginx-lua-prometheus
# 精细指标:每个 location 的 QPS、延迟分布、状态码分布
Prometheus + Grafana 监控面板关键指标:
| 指标 | 来源 | 告警阈值(参考) |
|---|---|---|
| 活跃连接数 | stub_status | 接近 worker_connections × worker_processes 的 80% |
| 请求延迟 P99 | 自定义日志 / lua | 超过基线 3 倍 |
| 上游响应时间 | $upstream_response_time | P99 > 2s |
| 4xx/5xx 比率 | access_log | 5xx > 1% |
| 缓存命中率 | $upstream_cache_status | MISS 率突然升高 |
| 限流触发次数 | limit_req 日志 | 持续增长 |
九、生产调优检查清单
| # | 检查项 | 推荐值 | 优先级 |
|---|---|---|---|
| 1 | worker_processes | auto 或 CPU 核心数 |
🔴 必须 |
| 2 | worker_connections | ≥ 4096 | 🔴 必须 |
| 3 | ulimit -n | ≥ 65535 | 🔴 必须 |
| 4 | upstream keepalive | ≥ 32 | 🔴 必须 |
| 5 | proxy_http_version 1.1 | 1.1 |
🔴 必须 |
| 6 | ssl_session_cache | shared:SSL:10m |
🔴 必须 |
| 7 | ssl_protocols | TLSv1.2 TLSv1.3 | 🔴 必须 |
| 8 | ssl_stapling | on |
🟡 推荐 |
| 9 | gzip on | comp_level 4-6 | 🔴 必须 |
| 10 | sendfile + tcp_nopush + tcp_nodelay | 全部 on |
🔴 必须 |
| 11 | server_tokens off | off |
🔴 必须 |
| 12 | 安全响应头 | X-Frame-Options 等 | 🔴 必须 |
| 13 | limit_req 限流 | 按业务设定 | 🟡 推荐 |
| 14 | proxy_cache | 适合的场景开启 | 🟡 推荐 |
| 15 | 自定义日志格式 | 含 request_time | 🟡 推荐 |
| 16 | stub_status 监控 | 开启 + Prometheus | 🟡 推荐 |
| 17 | HTTP/2 | listen 443 ssl http2 |
🟡 推荐 |
| 18 | Brotli | 安装模块后开启 | 🟢 可选 |
| 19 | proxy_cache_lock | on |
🟢 可选 |
| 20 | HTTP/3 (QUIC) | Nginx 1.25+ | 🟢 可选 |
十、速查表
| 场景 | 关键配置 | 要点 |
|---|---|---|
| 高并发基础 | worker auto + connections 4096 + multi_accept | 确认 ulimit -n 足够大 |
| 连接复用 | upstream keepalive + HTTP/1.1 | proxy_set_header Connection "" |
| SSL 加速 | session_cache + stapling + TLS 1.3 | 优先 ECDHE + AEAD 密码套件 |
| 负载均衡 | least_conn + health check | 避免 ip_hash,用 consistent hash 替代 |
| 缓存加速 | proxy_cache + cache_lock + stale | add_header X-Cache-Status 调试 |
| 压缩传输 | gzip level 4 + brotli + sendfile | 小文件(<1KB)不压缩 |
| 限流防护 | limit_req burst+nodelay + limit_conn | 返回 429 而非 503 |
| 安全加固 | server_tokens off + 安全头 + HSTS | 生产级用 ModSecurity |
| 监控可观测 | 自定义日志 + stub_status + Prometheus | 重点关注 P99 延迟和 5xx 率 |
| SSE/WebSocket | proxy_buffering off | 必须关闭,否则客户端延迟 |
| 大文件下载 | proxy_buffering off + directio | 避免 Nginx 内存撑爆 |
worker 调度最大化 + 连接复用(keepalive) + SSL 会话复用 + 缓存防击穿 + 压缩减带宽 + 限流保后端 + 监控看全局。每一层省 10%,整体就是数量级的提升。