为什么需要反向代理?
在生产环境中,我们很少让应用直接暴露给用户。反向代理(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-IP 和 X-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 作为反向代理的核心能力可以总结为:
- 反向代理:隐藏后端,统一入口
- 负载均衡:支持轮询、加权、IP Hash、最少连接等多种策略
- HTTPS:Let’s Encrypt 免费证书 + 安全加固
- 限流:保护后端免受恶意流量冲击
- 健康检查:自动故障转移,保障高可用
在实际项目中,建议先用基础配置上线,再根据监控数据逐步调整超时参数、限流阈值和负载均衡策略。配置变更前务必执行 nginx -t 检查语法,避免因配置错误导致服务中断。