Nginx 反向代理与负载均衡从入门到生产实践
前言
Nginx 不仅是一个高性能的 Web 服务器,它还是一款功能强大的反向代理和负载均衡工具。对于个人站长来说,掌握 Nginx 反向代理配置可以解决很多实际问题:在同一台服务器上运行多个网站、对后端应用进行流量分发、实现 HTTPS 统一终止和 WebSocket 代理等。本文将从实际场景出发,带你彻底掌握 Nginx 反向代理与负载均衡的核心配置。
一、反向代理基础概念
反向代理(Reverse Proxy)位于客户端和后端服务器之间,接收客户端的请求并将其转发给后端服务器。与正向代理(代理客户端访问外网)不同,反向代理对客户端是透明的——客户端不知道请求被转发给了哪个后端服务器。
反向代理的主要用途包括:
- 负载均衡:将请求分发到多台后端服务器
- 统一 SSL 终止:在后端服务器前面统一处理 HTTPS
- 缓存加速:缓存后端服务器的响应,减轻后端压力
- 安全防护:隐藏后端服务器的真实 IP
- 域名和路径路由:根据域名或路径将请求转发到不同的后端服务
二、基础反向代理配置
最基础的反向代理配置如下:
server {
listen 80;
server_name blog.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 指令指定后端服务器的地址。需要特别注意,proxy_pass 的目标地址可以带 URI 也可以不带,这两种情况的行为是不同的:
# 不带 URI:将完整的原始请求路径转发给后端
proxy_pass http://127.0.0.1:8080;
# 请求 /api/login -> 转发到 http://127.0.0.1:8080/api/login
# 带 URI:替换匹配的路径部分
proxy_pass http://127.0.0.1:8080/;
# 请求 /api/login -> 转发到 http://127.0.0.1:8080/login三、常用代理头说明
在配置反向代理时,正确设置请求头至关重要:
| 请求头 | 作用 | 推荐值 |
|---|---|---|
Host | 传递原始域名,后端据此区分虚拟主机 | $host |
X-Real-IP | 传递客户端真实 IP | $remote_addr |
X-Forwarded-For | 记录代理链路中的所有 IP | $proxy_add_x_forwarded_for |
X-Forwarded-Proto | 传递原始协议(HTTP/HTTPS) | $scheme |
如果后端应用需要获取客户端真实 IP(例如 WordPress 的评论显示 IP),务必正确设置以上头部:
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;四、负载均衡配置实战
当你的网站流量增长到一台服务器无法承受时,就需要使用负载均衡将流量分发到多台服务器。Nginx 的 upstream 模块提供了多种负载均衡算法:
# 定义后端服务器组
upstream backend {
# 默认算法:轮询(round-robin)
server 192.168.1.10:8080 weight=3;
server 192.168.1.11:8080 weight=2;
server 192.168.1.12:8080 backup;
}
server {
listen 80;
server_name www.example.com;
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;
}
}4.1 负载均衡算法选择
Nginx 支持以下负载均衡算法:
轮询(Round Robin):默认算法,按顺序将请求分发到各服务器。适用于所有服务器性能相近的场景。
加权轮询(Weighted):通过 weight 参数分配权重,权重越高的服务器接收更多请求。适用于服务器性能不均的场景。
最少连接(Least Connections):将请求发送到当前活跃连接数最少的服务器。适用于长连接应用:
upstream backend {
least_conn;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}IP Hash:根据客户端 IP 的哈希值分配服务器,保证同一客户端的请求始终发送到同一台服务器(会话保持):
upstream backend {
ip_hash;
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}4.2 健康检查与故障转移
Nginx 提供了被动的健康检查功能,当某台服务器连续返回错误时,自动将其标记为不可用:
upstream backend {
server 192.168.1.10:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.11:8080 max_fails=3 fail_timeout=30s;
}max_fails=3:连续失败 3 次后标记不可用fail_timeout=30s:标记不可用后,等待 30 秒再尝试恢复
而主动健康检查需要 Nginx Plus 的付费版本。作为替代方案,可以使用 nginx-upsync 模块或通过脚本定期检查后端状态。
五、多站点反向代理配置(个人站长实用场景)
对于个人站长来说,最常见的场景是在一台服务器上运行多个不同技术栈的网站。以下是一个在生产环境中经过验证的配置模板:
# WordPress 站点(PHP-FPM)
server {
listen 443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/nginx/ssl/blog.example.com.pem;
ssl_certificate_key /etc/nginx/ssl/blog.example.com.key;
root /var/www/wordpress;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}
# Node.js 应用(反向代理)
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/nginx/ssl/app.example.com.pem;
ssl_certificate_key /etc/nginx/ssl/app.example.com.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_read_timeout 86400s;
}
# 静态文件直接由 Nginx 处理
location /static/ {
alias /var/www/app/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
}
# Python Flask/Django 应用(通过 Gunicorn)
server {
listen 443 ssl http2;
server_name tools.example.com;
ssl_certificate /etc/nginx/ssl/tools.example.com.pem;
ssl_certificate_key /etc/nginx/ssl/tools.example.com.key;
location / {
proxy_pass http://127.0.0.1:8000;
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;
client_max_body_size 100m;
}
}六、HTTPS 统一终止与证书管理
在反向代理架构中,通常由 Nginx 统一处理 HTTPS,后端服务器只需要处理 HTTP。这样做的好处是证书管理集中、性能更优(Nginx 的 SSL 处理比大多数应用服务器快):
# HTTP -> HTTPS 重定向
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS 配置
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# 使用 Let's Encrypt 免费证书
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 off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 5s;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Proto https;
}
}七、WebSocket 代理配置
越来越多的网站使用 WebSocket 实现实时功能(在线聊天、实时通知、协同编辑)。Nginx 从 1.3 版本开始支持 WebSocket 代理:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream ws_backend {
server 127.0.0.1:3000;
}
server {
listen 443 ssl http2;
server_name chat.example.com;
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# WebSocket 通常需要较长的超时时间
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}关键点说明:
proxy_http_version 1.1:WebSocket 需要 HTTP/1.1 协议proxy_set_header Upgrade $http_upgrade:传递 Upgrade 请求头proxy_set_header Connection "upgrade":将 Connection 头设为 upgradeproxy_read_timeout:建议设置为较长时间(3600s 以上),防止长连接被意外断开
八、缓存配置加速后端响应
反向代理缓存可以大幅减少后端服务器的压力。对于不经常变化的页面,缓存效果非常明显:
# 定义缓存路径和参数
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=mycache:10m max_size=1g inactive=60m;
server {
listen 80;
server_name blog.example.com;
location / {
proxy_cache mycache;
proxy_cache_key $scheme$host$request_uri;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
# 跳过缓存的规则
proxy_cache_bypass $http_cache_control;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
# 管理员不缓存
location ~* /wp-admin|/admin|/login {
proxy_pass http://127.0.0.1:8080;
proxy_no_cache 1;
proxy_cache_bypass 1;
}
}通过 add_header X-Cache-Status 可以方便地调试缓存是否生效,响应头中的值:
HIT:命中缓存MISS:未命中缓存BYPASS:手动跳过缓存STALE:返回了过期的缓存(后端不可用时)UPDATING:正在更新缓存
九、常见问题与排查技巧
问题一:502 Bad Gateway
最常见的问题,通常表示后端服务器没有运行或无法连接:
# 检查后端服务是否运行
systemctl status your-backend-service
# 检查端口是否监听
ss -tlnp | grep 8080
# 检查 Nginx 错误日志
tail -f /var/log/nginx/error.log问题二:504 Gateway Timeout
表示 Nginx 等待后端服务器响应超时,通常需要调整超时时间:
location / {
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
proxy_pass http://backend;
}问题三:后端获取不到客户端真实 IP
确保以下头部已经正确设置:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;同时,后端应用需要从 X-Forwarded-For 头而不是 REMOTE_ADDR 获取客户端 IP。
总结
Nginx 反向代理是个人站长必须掌握的核心技能之一。从简单的单站点代理到复杂的多站点负载均衡,Nginx 提供了灵活而强大的配置能力。建议从最简单的配置开始,逐步增加缓存、SSL 终止、WebSocket 代理等功能。掌握这些技巧后,你的服务器架构将变得更加灵活、安全、高效。