记得刚入行那年,我最怕看到的画面不是满屏的代码报错,而是浏览器里那个冷冰冰的 “502 Bad Gateway”。
那时候觉得 Nginx 就是个黑盒,配置文件里的每一行指令都像是在对着服务器念咒语——念对了万事大吉,念错了一个分号,服务直接趴窝。我曾天真地以为,照着网上的教程把 proxy_pass 粘贴进去就算完事了,直到我经历了那几个心惊肉跳的深夜。
DevOps 的路上,我们大概率都会遇到这样的时刻:代码逻辑明明没问题,但请求就是发不到后端;或者明明加了机器做负载均衡,用户却总抱怨登录状态莫名丢失。
今天,我想把自己这几年记在备忘录里的 Nginx 实操经验翻出来,不讲晦涩的原理,只聊聊在真实业务场景下,怎么配置反向代理和负载均衡,才能让你睡个安稳觉。
别让“反向代理”成为黑洞:不仅是转发,更是“伪装”
很多新手(包括当年的我)在配置反向代理时,往往只写一行 proxy_pass。
我就踩过这样一个坑。当时我们有一个内部管理系统,后端用的 Spring Boot,前端 Nginx 做代理。上线第一天,运营同事就跑来吼:“为什么我生成的重定向链接,跳回的是 localhost?”
原来,当 Nginx 将请求转发给后端应用时,如果不做特殊配置,后端拿到的 Host 信息往往是 Nginx 所在的内网 IP 或者 localhost,而不是用户真正访问的域名。这导致后端在生成重定向 URL 或记录日志时,完全搞错了对象。
解决这个问题的关键,在于“透传”。 我们需要把用户真实的请求头信息,原封不动地告诉后端。
实战配置建议:
建议你把下面这几行配置,作为所有反向代理的“标配”。我习惯在公司内部维护一个 proxy_params 文件,每次直接 include 进去,省心又安全。
location /api/ {
proxy_pass http://127.0.0.1:8080;
# 核心:保留客户端真实的 Host 域名
proxy_set_header Host $host;
# 核心:传递客户端真实的 IP 地址,而不是 Nginx 的内网 IP
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 告诉后端当前的协议是 http 还是 https
proxy_set_header X-Forwarded-Proto $scheme;
}
自从加上这几行,后端日志里再也没出现过满屏的 127.0.0.1,排查问题时的效率提升了不止一倍。
负载均衡的“玄学”:不只是轮询,更要“专一”
随着业务量增长,单台服务器撑不住了,我们自然会想到加机器,上负载均衡。
大概两年前,我们要搞一个线上的抢购活动。为了保险,我临时加了两台服务器,配置了最简单的 upstream 轮询。我想着,三台机器平均分担压力,这下稳了吧?
结果活动刚开始五分钟,客服电话就被打爆了。用户反馈:“我刚登录进去,一点‘立即购买’,就提示我请先登录!”
即使是现在,回想起当时的冷汗直冒还心有余悸。排查后发现,问题出在 Session 上。默认的轮询策略(Round Robin)会把同一个用户的请求随机分发到不同的服务器上。用户在 A 服务器登录了,Session 存在 A 上;下一次点击被分发到了 B 服务器,B 上面没有 Session,自然就判定用户未登录。
虽然现在更推荐用 Redis 集中管理 Session,但在很多中小型项目或遗留系统中,IP 哈希(ip_hash) 依然是解决这个问题的最快救命稻草。
实战配置建议:
如果你的应用涉及用户登录状态,且没有做分布式的 Session 共享,请务必加上 ip_hash。它能保证同一个 IP 的请求固定打到同一台后端服务器上。
upstream backend_server_group {
# 关键指令:保证会话粘性
ip_hash;
# weight 代表权重,权重越高分配的请求越多
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=1;
# max_fails=3 fail_timeout=30s 代表:
# 如果这台机器在30秒内失败了3次,Nginx会在接下来的30秒内不再给它发请求
# 这就是简单的健康检查
server 192.168.1.103:8080 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server_group;
include proxy_params; # 记得加上上一节提到的标配
}
}
这个配置我用了很久,在很多不需要极致扩展性的中小项目中,它既简单又管用。
那些看不见的“隐形杀手”:超时与缓冲区
有时候,服务既没宕机,配置也没错,但用户体验就是极差。
我曾接手过一个旧项目,主要功能是文件上传。开发人员跟我抱怨:“小文件没事,一上传超过 1MB 的 Excel 表格,Nginx 直接报 413 错误;有时候报表生成慢一点,Nginx 就报 504 Gateway Time-out。”
这种挫败感很强,因为大家第一反应往往是代码写得烂,其实很多时候是 Nginx 的默认限制太“保守”了。Nginx 默认的请求体大小限制通常只有 1MB,这在富媒体时代显然不够用。
运维不仅要保稳定,更要懂业务场景。 遇到大文件上传或长耗时任务,我们需要给 Nginx 一点“耐心”。
实战配置建议:
不用盲目调大所有参数,建议针对特定的 URL 路径进行优化,避免全局配置带来的安全风险。
# 针对上传接口的特殊配置
location /api/upload {
proxy_pass http://backend_server_group;
# 允许客户端上传的最大文件大小,按需调整
client_max_body_size 100m;
# 增加后端连接和读取的超时时间
# 默认通常是60s,对于复杂业务逻辑可能不够
proxy_connect_timeout 300s;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
调整完这几个参数后,那个文件上传的功能瞬间顺滑了,开发同事看我的眼神都多了几分信任。
写在最后
技术这东西,往往是“纸上得来终觉浅”。
我看过无数篇《Nginx 权威指南》,但真正让我记住这些配置的,是那次重定向错误的尴尬,是用户被踢下线的焦灼,是上传失败时的无奈。每一个配置指令背后,其实都是一个真实的业务痛点。
如果你是刚开始接触 Nginx 的朋友,我想告诉你:不要怕报错。 打开 Nginx 的 error.log(通常在 /var/log/nginx/ 下),那里藏着最好的老师。每次配置变更后,记得用 nginx -t 测试一下语法,这是我们对自己工作的敬畏。
从今天起,不妨试着给你的 Nginx 配置文件做一次“体检”。
给你的 3 个落地行动建议:
- 查漏补缺: 检查你的
location /配置,是否加上了Host和Real-IP的透传头。 - 日志巡检: 去服务器上看一眼
error.log,有没有因为client_max_body_size导致的 413 错误被你忽略了? - 配置备份: 我习惯每周五把调试好的
nginx.conf备份到 Git 仓库,相信我,当你手抖误删配置时,这个备份会救你的命。
你在配置 Nginx 或其他网关工具时,遇到过什么让你“抓狂”的坑吗? 欢迎在评论区聊聊,说不定你的分享能帮另一位熬夜的兄弟少掉几根头发。