假设你只有一个后端服务,用户直接访问 http://your-backend:3000,一切正常。但突然用户变多了,一台服务器扛不住,你加了第二台,总不能让大家手动换地址吧?又或者你不想让用户直接接触你的后端 IP,想加一层缓存、限流、SSL 卸载?这时候 Nginx 就派上用场了。
打个比方,反向代理是给后端当“门卫”,所有请求先经过 Nginx,再由它转发给后端;负载均衡则是把请求分摊到多台后端服务器上,让系统更稳定、容量更大。
今天的教程我会直接给你能跑起来的配置,再聊几个我踩过的坑,最后给一些最佳实践。看完你就能自己搭一套生产可用的反向代理+负载均衡环境。
多数 Linux 发行版直接用包管理器装:
装完启动:
检查是否运行:curl -I http://localhost 应该返回 200 或 304。
现在假设你本地跑了一个 Node.js 服务,端口 3000。我们想通过 Nginx 把 /api 路径的请求都代理到它。
创建配置文件 /etc/nginx/conf.d/proxy.conf(或者直接在 sites-available 下写,随你习惯),内容如下:
proxy_pass 后面如果加了斜杠 /,表示会把 /api 后面的路径拼接到后端地址上;如果不加斜杠,则会把整个 /api 路径原样传给后端,容易弄混。举个例子:
/api/users,如果 proxy_pass http://127.0.0.1:3000/,则后端收到 /users。proxy_pass http://127.0.0.1:3000,则后端收到 /api/users。大多数情况你想去掉前缀,所以加斜杠。
测试配置:sudo nginx -t,没问题就 sudo systemctl reload nginx。
现在你访问 http://your-domain.com/api/users 就会转发到本地的 http://127.0.0.1:3000/users。
假设你有两台 Node.js 服务,分别运行在 3001 和 3002 端口。把请求平均分到它们身上。
在 proxy.conf 里增加一个 upstream 块:
上面这个配置,所有请求都会轮流打到 3001 和 3002。如果你需要保持用户 session(比如登录状态存后端内存里),就用 ip_hash,这样同一 IP 的请求始终打到同一台后端。但要注意,如果后端挂了,ip_hash 会导致流量集中到别的机器,Nginx 会自己处理 failover。
完整可运行示例:我把上面两个配置合并成一个文件,再加一点健康检查和超时设置:
把这段保存为 /etc/nginx/conf.d/api.conf,然后 nginx -t 检查,接着 systemctl reload nginx。
后端服务可以用简单的 Express 来测试:
启动两个服务,然后多次访问 http://api.example.com,你会看到轮流返回 server 1 和 server 2。
报错:浏览器返回 502,Nginx 错误日志显示 connect() failed (111: Connection refused) while connecting to upstream。
原因:后端服务没启动,或者 Nginx 配置的 upstream 地址写错了(比如端口不对、IP 写成了 localhost 但实际监听 127.0.0.1 却配了 0.0.0.0 等)。
解决:
ps aux | grep node。curl http://127.0.0.1:3001 看能否返回内容。proxy_pass 地址:upstream 块里如果用 localhost,在某些系统上会解析到 IPv6 ::1,而你的后端可能只监听 IPv4。经验之谈:建议统一用 127.0.0.1 而不是 localhost,避免 DNS 解析问题。报错:请求 /api/users 返回 404,或者看到的是 Nginx 欢迎页。
原因:location 匹配顺序问题,或者 proxy_pass 没有正确剥离路径前缀。
解决:
location。如果只配了 location /api/,但请求是 /api(无斜杠),则不会匹配。用 location /api(无斜杠)可以同时匹配 /api 和 /api/,但更推荐统一末尾带斜杠。proxy_pass 是否带斜杠。如果不带,后端收到的是 /api/users,而你的后端路由可能只定义了 /users,导致 404。其实:proxy_pass 末尾斜杠是最容易搞混的,建议先加斜杠测试,不行再改。现象:后端看到的 req.ip 总是 127.0.0.1 或 Nginx 的 IP。
原因:没有设置 proxy_set_header X-Real-IP 或 X-Forwarded-For。
解决:加上上面示例中的那几个 header。另外后端框架可能需要从这些 header 中读取真实 IP。比如 Express 可以安装 trust-proxy 中间件:
localhost 在 /etc/hosts 中可能同时映射到 IPv6(::1),如果后端只绑 IPv4,连接就失败。踩过这个坑的人都知道。resolver 和 set 变量来强制每次请求解析。配置示例:keepalive 32;,然后在 server 块的 location 里加 proxy_http_version 1.1; 和 proxy_set_header Connection ""; 来开启长连接。这能大幅减少 TIME_WAIT。max_fails 和 fail_timeout)。如果你需要主动健康检查,可以用第三方模块 nginx_upstream_check_module(淘宝版)或者商业版 Nginx Plus。免费方案可以写个脚本定期 curl 后端,如果挂了就修改 upstream 配置。简单点就用 max_fails=1 fail_timeout=5s 来快速踢掉故障机器。- 不要将 Nginx 直接暴露在互联网上而不做限制。至少加上 limit_req 模块防止 CC 攻击。
- 如果对外暴露的是 HTTPS,记得在 server 块配置 SSL,然后 proxy_pass 给后端的 HTTP。Nginx 负责 SSL 终结,减轻后端压力。
- 设置合理的 proxy_read_timeout 避免后端慢请求拖垮 Nginx 工作进程。
- 使用自定义日志格式记录 upstream 信息,排查问题时你会感谢自己。
- 配置 error_log 级别为 warn 以上,避免生产日志爆炸。
- 配合 Prometheus + nginx-lua-prometheus 或 ngx_http_stub_status_module 监控请求量。
- 调整 worker_processes 为 auto,worker_connections 根据内存适当加大(如 1024或2048)。
- 启用 sendfile 和 tcp_nopush。
- 如果后端返回大量静态文件,可以考虑在 Nginx 层加缓存:proxy_cache。
- 利用 upstream 的两个 server,先下线一个、更新、再上线;或者通过 weight=0 临时摘除节点。
- 更高级的可以用 $cookie_backend 或 $http_x_canary 变量动态路由到不同 upstream。
Nginx 反向代理和负载均衡的配置其实不复杂,核心就是 upstream + proxy_pass。但细节决定成败:斜杠、IP 地址写法、超时、header 传递、DNS 缓存……每一个小点都可能让你抓狂几小时。
今天给的示例你直接复制改一改就能用。建议先在本机用 Docker 起两个简单的 HTTP 服务测试,把配置调通后再上生产。
nginx -t 检查语法,然后用 curl -v http://你的地址 看响应头,确认请求是否正确转发。502错误通常是后端服务没启动或地址写错,504是超时问题。
最后,如果你用的是 HTTPS 站点,记得把 listen 80 改成 listen 443 ssl,再加几行 SSL 配置。代理到后端时,如果后端也是 HTTPS,则 proxy_pass 要写 https://...,并注意证书验证。这部分可以单独写一篇,今天就不展开了。
好了,动手试试吧。遇到问题多看 error.log,真相就在那里。