一台服务器上跑着好几个服务:博客、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 访问:
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;
}
}配置放好后验证并重载(每次改配置必做):
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 配置块(容易踩加密套件配置的坑):
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx -y
# 一条命令: 验证域名 → 签发证书 → 自动改 nginx 配置 → 自动配 80→443 跳转
sudo certbot --nginx -d app.example.comCertbot 会自动:把 listen 80 改成 listen 443 ssl http2、插入证书路径、加 301 跳转、配置安全套件。你只需要管一件事——自动续期:
sudo systemctl status certbot.timer # 证书 90 天有效, 定时器会提前 30 天自动续期
sudo certbot renew --dry-run # 手动演练一次续期, 确认没毛病证书文件在 /etc/letsencrypt/live/域名/ 下:fullchain.pem(证书链)和 privkey.pem(私钥),注意别泄露私钥。
负载均衡:一个入口,多个后端
后端服务部署了多份(多实例/多机器),用 upstream 定义服务器池,proxy_pass 指向池子:
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 转发,少了下面两行连接会莫名断开:
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 分发,互不干扰:
# /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 三大原因:
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; },把不存在的路径回退到入口页。
安全与性能小优化
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 是唯一的门,门背后是各种服务。按这个路径走:
- 最小
server块 + 四个请求头起步 - HTTPS 直接
certbot --nginx,续期交给定时器 - 多实例上
upstream,按场景选算法 - WebSocket 记得
proxy_http_version 1.1+Upgrade/Connection+ 长超时 - 每次改配置
nginx -t+reload,出问题先看 error.log
配置本身不难,难的是知道「哪些头不能省、哪些超时要调大」。把本文这几条记牢,反代基本不会翻车。
评论 (0)
暂无评论,快来抢沙发吧!