Nginx 反向代理性能调优:从入门到生产

Nginx 作为反向代理几乎是现代 Web 架构的标配。但很多人止步于 proxy_pass 的基本配置,默认参数跑起来能工作,一上量就出问题——502 频发、SSL 握手拖慢首字节、缓存没命中反而多了一跳、限流太粗暴误杀正常用户。

本文从反向代理基础概念出发,逐层深入到 worker 调优、负载均衡、SSL 加速、缓存策略、压缩传输、限流安全和监控体系,最后给出一份可直接用于生产环境的检查清单。

环境说明:本文基于 Nginx 1.26+ / OpenSSL 3.x,配置适用于 Linux 系统。macOS 和 BSD 部分参数略有差异,会在文中标注。

一、反向代理基础概念

正向代理 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_connectionsulimit -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/大文件下载
}
proxy_buffering 什么时候关?:Server-Sent Events(SSE)、WebSocket、实时流媒体场景必须关闭,否则 Nginx 会攒够一个 buffer 再转发,导致客户端延迟飙升。大文件下载也可以关,避免 Nginx 内存被撑爆。

三、负载均衡策略

五种策略对比

策略 指令 特点 适用场景
轮询(默认) (默认,无需指定) 均匀分配,不考虑服务器差异 后端机器配置相同
加权轮询 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)
ip_hash 的陷阱:IPv4 前 3 段相同会被哈希到同一后端,企业 NAT 出口可能导致流量严重不均。而且增减后端节点时,哈希重分布比例较大。尽量用 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
}
为什么优先 AES-128?:AES-128-GCM 比 AES-256-GCM 快约 30%(硬件 AES-NI 加速下),且安全性在可预见的未来足够。CHACHA20 在没有 AES-NI 的设备(老手机、ARM)上更快。TLS 1.3 默认只保留这五个套件。

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;
}
stale-while-revalidate 的价值:高流量场景下,缓存过期的瞬间可能有数百个并发请求打到上游。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 94 压缩率只多 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 + nodelay 的配合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;
    }
}
生产级 WAF:以上只是基础防护。对于真正的 Web 攻击(SQL 注入、XSS、CC 攻击),建议使用 ModSecurity + OWASP Core Rule Set,或云 WAF 服务(Cloudflare、AWS WAF 等)。

八、日志与监控

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 内存撑爆
一句话总结:Nginx 反向代理调优的核心公式——worker 调度最大化 + 连接复用(keepalive) + SSL 会话复用 + 缓存防击穿 + 压缩减带宽 + 限流保后端 + 监控看全局。每一层省 10%,整体就是数量级的提升。