Nginx 反向代理与负载均衡从入门到生产实践

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;
    }
}

关键点说明:

  1. proxy_http_version 1.1:WebSocket 需要 HTTP/1.1 协议
  2. proxy_set_header Upgrade $http_upgrade:传递 Upgrade 请求头
  3. proxy_set_header Connection "upgrade":将 Connection 头设为 upgrade
  4. proxy_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 代理等功能。掌握这些技巧后,你的服务器架构将变得更加灵活、安全、高效。

Last modification:July 28th, 2026 at 08:11 am

Leave a Comment