Nginx 反向代理实战：从入门配置到 HTTPS 与负载均衡
一台服务器上跑着好几个服务：博客、API、监控面板……每个都监听一个端口（3000、8080、9090），用户记不住端口号，浏览器地址栏还老是弹「不安全」。反代（反向代理）就是来解决这件事的：**所有流量先到 Nginx，Nginx 按域名/路径转发给背后的服务**，端口、证书、负载均衡全部在这一层集中处理。本文用一套「从零到生产」的实战路径讲清 Nginx 反向代理：最小配置 → HTTPS → 负载均衡 → WebSocket → 多应用部署 → 常见坑。## 反向代理原理：一句话用户访问 `https://example.com` → Nginx 收到请求 → 根据 `server_name` 和 `location` 规则 → 转发给内网/本机的后端服务 → 拿回响应再返回给用户。**对用户来说只看到 Nginx 这一个入口，后端服务的真实地址被藏了起来。**## 最小可用配置假设你有个应用跑在 `127.0.0.1:3000`，想让用户通过 `app.example.com` 访问：```nginx server { listen 80; server_name app.example.com;location / { 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; } } ```配置放好后验证并重载（**每次改配置必做**）：```bash sudo nginx -t # 语法检查 sudo systemctl reload nginx # 平滑重载, 不中断连接 ```### 四个请求头，一个都不能少这四行是生产环境标配，缺了会出各种诡异问题：| 请求头 | 作用 | 缺失后果 | | --- | --- | --- | | `Host $host` | 把原始域名传给后端 | 后端按域名路由时会 404 | | `X-Real-IP $remote_addr` | 传递真实客户端 IP | 后端日志里全是 127.0.0.1 | | `X-Forwarded-For` | 完整代理链 IP 列表 | 多级代理时拿不到真实 IP | | `X-Forwarded-Proto $scheme` | 告知后端是 http 还是 https | 应用生成错误的跳转（http→http 死循环） |## HTTPS：交给 Certbot，别手写证书配置申请 Let's Encrypt 证书用 Certbot 的 Nginx 插件一键完成，**不要手动生成证书和写 SSL 配置块**（容易踩加密套件配置的坑）：```bash # Ubuntu/Debian sudo apt install certbot python3-certbot-nginx -y# 一条命令: 验证域名 → 签发证书 → 自动改 nginx 配置 → 自动配 80→443 跳转 sudo certbot --nginx -d app.example.com ```Certbot 会自动：把 `listen 80` 改成 `listen 443 ssl http2`、插入证书路径、加 301 跳转、配置安全套件。你只需要管一件事——**自动续期**：```bash sudo systemctl status certbot.timer # 证书 90 天有效, 定时器会提前 30 天自动续期 sudo certbot renew --dry-run # 手动演练一次续期, 确认没毛病 ```证书文件在 `/etc/letsencrypt/live/域名/` 下：`fullchain.pem`（证书链）和 `privkey.pem`（私钥），注意别泄露私钥。## 负载均衡：一个入口，多个后端后端服务部署了多份（多实例/多机器），用 `upstream` 定义服务器池，`proxy_pass` 指向池子：```nginx upstream app_cluster { least_conn; # 算法: 见下方说明 server 192.168.1.101:9090 weight=3; # 权重 3, 分到更多流量 server 192.168.1.102:9090 weight=2; server 192.168.1.103:9090 max_fails=3 fail_timeout=30s; # 30 秒内失败 3 次则摘除 server 192.168.1.104:9090 backup; # 备用机, 主服务器全挂才启用 }server { listen 443 ssl; server_name api.example.com; location / { proxy_pass http://app_cluster; 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; } } ```**四种负载均衡算法：**| 算法 | 说明 | 适用场景 | | --- | --- | --- | | 轮询（默认） | 依次分发，可配 `weight` 权重 | 后端性能均衡 | | `least_conn` | 转发给连接数最少的后端 | 请求处理时间差异大 | | `ip_hash` | 同一 IP 永远打到同一后端 | 需要会话保持（WebSocket/登录态） | | `random two least_conn` | 随机挑两个取连接少的 | 后端数量多 |## WebSocket：缺两行就静默失败WebSocket 需要 HTTP/1.1 升级握手，**`proxy_pass` 默认按 HTTP/1.0 转发，少了下面两行连接会莫名断开**：```nginx location /ws/ { proxy_pass http://backend; proxy_http_version 1.1; # 升级协议必需 proxy_set_header Upgrade $http_upgrade; # 传递升级请求 proxy_set_header Connection "upgrade"; # 声明连接升级 proxy_set_header Host $host; proxy_read_timeout 3600s; # 关键! 默认 60s 会掐断长连接 } ````proxy_read_timeout` 不调大，WebSocket 长连接 60 秒后就会被 Nginx 断开——这是最常见的「连接一会儿就断」原因。多实例部署 WebSocket 建议配 `ip_hash`，保证同一客户端的连接始终落在同一后端。## 多应用同机部署：按域名路由一台服务器多个应用，各写各的 `server` 块，Nginx 按 `server_name` 分发，互不干扰：```nginx # /etc/nginx/sites-available/blog.conf server { listen 443 ssl; server_name blog.example.com; root /var/www/blog; # WordPress 伪静态: 非真实文件交给 index.php 处理 location / { try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:/run/php/php8.2-fpm.sock; } }# /etc/nginx/sites-available/api.conf server { listen 443 ssl; server_name api.example.com; location / { proxy_pass http://127.0.0.1:8080; } } ```启用站点：`sudo ln -s /etc/nginx/sites-available/blog.conf /etc/nginx/sites-enabled/` 然后 `nginx -t` + `reload`。**实战提醒**：面板类工具（宝塔等）生成的伪静态规则偶尔会「好心办坏事」——比如把 `/category/xxx/` 这类路径拦截掉导致 404。遇到这种问题，先看面板的伪静态/重写规则，再看 nginx 的 `location` 匹配优先级，别急着怀疑应用本身。## 常见坑与排查**502 Bad Gateway 三大原因：**```bash systemctl status 你的服务 # 1. 后端根本没启动 ss -tlnp | grep 3000 # 2. proxy_pass 端口写错/没监听 tail -f /var/log/nginx/error.log # 3. 看真实报错(连接拒绝/超时) ```**HTTPS 证书没生效 / 跳转异常**：先 `curl -v https://域名` 看证书链，`sudo certbot certificates` 查证书状态，`nginx -t` 确认配置没被改坏。**前端路由刷新 404（SPA）**：`location / { try_files $uri $uri/ /index.html; }`，把不存在的路径回退到入口页。## 安全与性能小优化```nginx http { server_tokens off; # 隐藏 Nginx 版本号# 限流: 每个 IP 每秒 5 个请求, 超出返回 503 limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s; }server { # 安全响应头 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Strict-Transport-Security "max-age=31536000" always; # 强制 HTTPS# 屏蔽敏感文件 location ~ \.(env|git|log|sql)$ { deny all; }# 静态资源直接由 Nginx 出, 不代理给后端 location /static/ { alias /var/www/app/static/; expires 1y; add_header Cache-Control "public, immutable"; } } ```多个 server 块共享的代理头可以抽到 `/etc/nginx/proxy_params` 文件里，用 `include proxy_params;` 复用，少写重复代码。## 总结反向代理的核心心智模型就一句话：**Nginx 是唯一的门，门背后是各种服务**。按这个路径走：1. 最小 `server` 块 + 四个请求头起步 2. HTTPS 直接 `certbot --nginx`，续期交给定时器 3. 多实例上 `upstream`，按场景选算法 4. WebSocket 记得 `proxy_http_version 1.1` + `Upgrade`/`Connection` + 长超时 5. 每次改配置 `nginx -t` + `reload`，出问题先看 error.log配置本身不难，难的是知道「哪些头不能省、哪些超时要调大」。把本文这几条记牢，反代基本不会翻车。## 参考资料- [How to Set Up a Reverse Proxy with Nginx (MeshWorld)](https://meshworld.netlify.app/blog/how-to/setup-nginx-reverse-proxy/) - [使用 Nginx 部署反向代理并使用 Certbot 自动配置和续期 SSL](https://www.wxy97.com/archives/019b87be-557e-727f-806c-98c883977ed9) - [Nginx Reverse Proxy, Load Balancer, and SSL Termination Guide (KX)](https://kx.cloudingenium.com/en/nginx-reverse-proxy-load-balancer-ssl-guide/) - [NGINX 技术全解析：从入门到实战的完整指南](https://developer.baidu.com/article/detail.html?id=5928190)