Contents

Nginx 反向代理、负载均衡与 HTTPS 配置实战

为什么需要反向代理?

在生产环境中,我们很少让应用直接暴露给用户。反向代理(Reverse Proxy)是架构中的关键一环:

  • 安全隔离:后端服务不直接暴露端口,降低攻击面
  • 负载均衡:将流量分发到多个后端实例
  • SSL 终止:统一处理 HTTPS 加解密,后端只需处理 HTTP
  • 缓存加速:静态资源直接由 Nginx 返回,减轻应用压力
  • 压缩传输:gzip/brotli 压缩响应体,节省带宽

本文从实际场景出发,覆盖 Nginx 最常用的三大能力。

一、基础反向代理

场景:将 /api 路径代理到后端服务

假设你的 Node.js 应用运行在 127.0.0.1:3000

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
server {
    listen 80;
    server_name api.example.com;

    # 反向代理到后端
    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        
        # 传递真实客户端信息
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # 超时设置
        proxy_connect_timeout 10s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # 静态资源由 Nginx 直接服务
    location /static/ {
        alias /var/www/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

关键点

  • proxy_pass 末尾的 / 会影响路径转发行为。proxy_pass http://127.0.0.1:3000; 会转发完整 URI,而 proxy_pass http://127.0.0.1:3000/; 会去掉 location 匹配的前缀
  • X-Real-IPX-Forwarded-For 让后端能获取真实客户端 IP,否则所有请求都显示 Nginx 的 IP

二、负载均衡

2.1 基础轮询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
upstream backend {
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

默认就是轮询(Round Robin),每个请求依次分配到不同后端。

2.2 加权轮询

当服务器性能不同时,用权重分配流量:

1
2
3
4
5
6
upstream backend {
    # 高性能服务器分配更多流量
    server 127.0.0.1:3001 weight=5;
    server 127.0.0.1:3002 weight=3;
    server 127.0.0.1:3003 weight=2;
}

2.3 IP Hash 会话保持

需要会话保持(Session Sticky)时,用 IP Hash 让同一客户端始终访问同一后端:

1
2
3
4
5
6
upstream backend {
    ip_hash;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

2.4 最少连接

将请求分配给当前连接数最少的后端,适合处理时间差异大的场景:

1
2
3
4
5
6
upstream backend {
    least_conn;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    server 127.0.0.1:3003;
}

2.5 健康检查与故障转移

1
2
3
4
5
upstream backend {
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3003 backup;  # 备用服务器,仅在其他都不可用时启用
}
  • max_fails=3:连续失败 3 次后标记为不可用
  • fail_timeout=30s:标记不可用后 30 秒重新尝试
  • backup:仅在所有主服务器不可用时才使用

主动健康检查(Nginx Plus 商业版或 OpenResty 支持):

1
2
3
4
5
6
# OpenResty / lua-resty-upstream-healthcheck
upstream backend {
    zone backend 64k;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
}

对于开源版 Nginx,可以配合 nginx_upstream_check_module 模块实现。

三、HTTPS 配置

3.1 Let’s Encrypt 免费证书

1
2
3
4
5
6
7
8
# 安装 certbot
sudo apt install certbot python3-certbot-nginx

# 自动获取证书并配置 Nginx
sudo certbot --nginx -d example.com -d www.example.com

# 测试自动续期
sudo certbot renew --dry-run

3.2 完整 HTTPS 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# HTTP → HTTPS 重定向
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # SSL 证书
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # 安全加固
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS(启用后浏览器会强制 HTTPS,谨慎使用)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # 安全头
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;

    location / {
        proxy_pass http://backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

四、限流配置

防止恶意请求打垮后端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
http {
    # 定义限流区域:每个 IP 每秒 10 个请求
    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    
    # 连接数限制
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;

    server {
        listen 443 ssl http2;
        
        # API 限流:burst 允许突发 20 个请求,超出返回 503
        location /api/ {
            limit_req zone=api_limit burst=20 nodelay;
            limit_conn conn_limit 50;  # 每 IP 最大 50 个并发连接
            
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

限流参数说明

参数 含义
rate=10r/s 每秒允许 10 个请求(即每 100ms 一个)
burst=20 突发容量,队列中最多排队 20 个请求
nodelay 突发请求不排队,直接处理(超过 burst 立即返回 529)
zone=api_limit:10m 共享内存区域,10MB 可存储约 16 万个 IP

五、实战:完整生产配置

将以上所有配置整合成一个可直接使用的模板:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# /etc/nginx/conf.d/production.conf

upstream app {
    least_conn;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3002 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3003 backup;
    
    keepalive 32;  # 与后端保持长连接
}

limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_session_cache shared:SSL:10m;

    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml;
    gzip_min_length 1000;

    # 静态资源
    location /static/ {
        alias /var/www/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # API 代理
    location /api/ {
        limit_req zone=api burst=30 nodelay;
        
        proxy_pass http://app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        proxy_connect_timeout 5s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # 默认路由
    location / {
        proxy_pass http://app;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

配置检查与热重载

1
2
3
4
5
# 检查语法
sudo nginx -t

# 平滑重载(不断开现有连接)
sudo nginx -s reload

六、调试与排错

查看 Nginx 访问日志

1
2
3
4
5
6
7
8
# 实时跟踪请求
tail -f /var/log/nginx/access.log

# 统计各状态码数量
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn

# 查找慢请求(响应时间 > 1s)
awk '$NF > 1.0' /var/log/nginx/access.log

常见问题排查

现象 原因 解决方案
502 Bad Gateway 后端服务未启动或端口不对 检查后端进程:curl localhost:3001
504 Gateway Timeout 后端处理太慢 增大 proxy_read_timeout,或优化后端
413 Request Entity Too Large 请求体超过限制 添加 client_max_body_size 50m;
SSL 证书警告 证书过期或配置错误 certbot renew,检查证书路径

总结

Nginx 作为反向代理的核心能力可以总结为:

  1. 反向代理:隐藏后端,统一入口
  2. 负载均衡:支持轮询、加权、IP Hash、最少连接等多种策略
  3. HTTPS:Let’s Encrypt 免费证书 + 安全加固
  4. 限流:保护后端免受恶意流量冲击
  5. 健康检查:自动故障转移,保障高可用

在实际项目中,建议先用基础配置上线,再根据监控数据逐步调整超时参数、限流阈值和负载均衡策略。配置变更前务必执行 nginx -t 检查语法,避免因配置错误导致服务中断。