Nginx 反向代理实战:从单台 Web 到负载均衡集群
反向代理是互联网架构里最基础也最关键的一环。一个小小的 Nginx,既能扛住百万级并发,也能让你的服务架构变得灵活可扩展。本文从最基础的反向代理讲起,逐步深入到负载均衡、HTTPS 配置、性能调优,手把手带你成为 Nginx 高手。
什么是反向代理
先区分三个概念:
- 正向代理:代理客户端(如 VPN、科学上网工具)
- 反向代理:代理服务端(用户无感知的”中间人”)
- 透明代理:两者结合,客户端和服务端都不知道
反向代理的核心价值:
- 隐藏真实服务:用户只看到代理服务器的 IP
- 负载均衡:把请求分发到多台后端
- SSL 卸载:HTTPS 加密解密在代理层完成
- 缓存加速:静态资源走代理,减轻后端压力
- 安全防护:可以过滤恶意请求、做 WAF
第一步:最基础的反向代理
场景
把请求 http://proxy.example.com/ 转发到后端 http://127.0.0.1:8080/。
配置
1 2 3 4 5 6 7 8 9 10 11 12
| server { listen 80; server_name proxy.example.com;
location / { proxy_pass http://127.0.0.1:8080; 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_pass:转发目标,支持 HTTP/HTTPS/upstream 块proxy_set_header Host $host:必须加,否则后端拿到的 Host 是 proxy_pass 里的地址X-Real-IP:传递客户端真实 IP,后端可以通过这个字段获取X-Forwarded-For:IP 链路,多级代理时会一直追加
第二步:负载均衡
upstream 块配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| upstream backend_pool { ip_hash;
server 192.168.1.10:8080 weight=3; server 192.168.1.11:8080 weight=2; server 192.168.1.12:8080 weight=1; server 192.168.1.13:8080 backup; }
server { listen 80; server_name lb.example.com;
location / { proxy_pass http://backend_pool; } }
|
负载均衡算法
健康检查
Nginx 自带的是被动健康检查(某台后端连不上就标记 down,但下次还会尝试)。
想要主动健康检查,可以使用 nginx_upstream_check_module:
1 2 3 4 5 6 7 8
| upstream backend_pool { server 192.168.1.10:8080; server 192.168.1.11:8080;
check interval=3000 rise=2 fall=5 timeout=1000 type=http; check_http_send "HEAD /health HTTP/1.0\r\n\r\n"; check_http_expect_alive http_2xx http_3xx; }
|
或者用 Tengine(淘宝基于 Nginx 的增强版),自带主动健康检查。
第三步:HTTPS 配置
申请证书(Let’s Encrypt 免费)
1 2 3 4 5
| yum install -y certbot python-certbot-nginx
certbot --nginx -d example.com -d www.example.com
|
Nginx 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
| server { listen 443 ssl http2; server_name 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_ciphers '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;
add_header Strict-Transport-Security "max-age=31536000" always;
location / { proxy_pass http://backend_pool; } }
server { listen 80; server_name example.com; return 301 https://$host$request_uri; }
|
第四步:动静分离
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| server { listen 80; server_name static.example.com;
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2?)$ { root /var/www/static; expires 30d; add_header Cache-Control "public, immutable"; }
location / { proxy_pass http://backend_pool; } }
|
第五步:WebSocket 代理
1 2 3 4 5 6 7
| location /ws { proxy_pass http://backend_pool; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 600s; }
|
第六步:性能调优
worker 配置
1 2 3 4 5 6 7 8 9 10
| user www www; worker_processes auto; worker_rlimit_nofile 65535;
events { worker_connections 10240; use epoll; multi_accept on; }
|
理论最大并发 = worker_processes × worker_connections,但还要除以 2(HTTP 请求一般有客户端连接 + 后端连接)。
缓冲区调优
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| http { client_body_buffer_size 128k; client_max_body_size 50m; client_header_buffer_size 1k; large_client_header_buffers 4 8k;
proxy_buffer_size 16k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; }
gzip on; gzip_min_length 1k; gzip_comp_level 6; gzip_types text/plain text/css text/javascript application/json application/javascript application/xml; gzip_vary on;
|
开启 HTTP/2
HTTP/2 多路复用,性能大幅提升,前面配置已经加了 http2。
连接复用
1 2 3 4 5 6 7 8 9 10
| upstream backend_pool { server 192.168.1.10:8080; keepalive 32; }
location / { proxy_pass http://backend_pool; proxy_http_version 1.1; proxy_set_header Connection ""; }
|
第七步:日志分析与监控
自定义日志格式
1 2 3 4 5 6 7
| log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for" ' 'rt=$request_time uct="$upstream_connect_time" ' 'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
|
重点关注三个时间:
$request_time:整个请求处理时间$upstream_response_time:后端响应时间$upstream_connect_time:与后端建立连接的时间
日志切割
1 2 3 4 5 6 7
| #!/bin/bash
LOG_DIR="/var/log/nginx" YESTERDAY=$(date -d "yesterday" +%Y%m%d) mv ${LOG_DIR}/access.log ${LOG_DIR}/access_${YESTERDAY}.log mv ${LOG_DIR}/error.log ${LOG_DIR}/error_${YESTERDAY}.log nginx -s reload
|
加入 crontab:
1
| 0 0 * * * /bin/bash /usr/local/sbin/nginx_log_split.sh
|
常见问题排查
502 Bad Gateway
后端连不上。检查:
- 后端服务是否启动
- 网络是否通:
telnet 192.168.1.10 8080 - Nginx 错误日志:
tail -f /var/log/nginx/error.log
504 Gateway Timeout
后端响应超时。增大超时时间:
1 2 3
| proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s;
|
但最好还是排查后端为什么慢。
客户端真实 IP 丢失
确保配置了 X-Real-IP 和 X-Forwarded-For,并且后端应用正确读取这两个 header。
总结
Nginx 看似简单,实则深不见底。本文带你走完了从基础到进阶的核心知识点:
学 Nginx 没有捷径,就是多配、多查文档、多踩坑。建议大家把本文的配置都亲手敲一遍,然后在自己博客或者测试服务器上跑起来,这样才能真正掌握。