说实话,以前我一直觉得 Nginx 是个 “傻瓜工具”。
不就是反向代理、填两行配置、重启服务就能用吗?网上模板一抓一大把,随便抄一抄就能把服务跑起来。很长一段时间里,我都把它当成一个零难度、零 Bug、稳定得不需要思考的转发工具。
直到我开始大规模自建服务、部署用户中心、对接 SSO 单点登录、跑 OpenWebUI 流式对话、搭 WebSocket 实时服务之后,我彻底被打服了。
我遇到了一堆完全不讲道理的问题。
服务没崩、日志没报错、状态码全是 200、本地运行完美、抓包前端请求完整。 只要挂上 Nginx 代理,业务直接乱套。
登录态莫名消失、自定义 Token 凭空不见、AI 流式对话每隔一分钟准时断线、页面改了代码永远不更新、后端拿到的用户 IP 全是本地 [127.0.0.1](127.0.0.1)。
最让人崩溃的是,这些问题不是必现,是偶发玄学。 有时候刷新十次都没事,换个网络、过五分钟直接翻车。
为了搞定这些隐形坑,我前后断断续续排查了小半年,翻遍了国内外论坛、啃官方文档、对比无数错误配置、一步步注释测试。 到最后我才明白:Nginx 没有 Bug,但它的默认行为,能把不懂原理的开发者逼疯。
今天这篇文章,不讲教科书套话,不堆砌官方文档。 只讲我真实踩过、真的折磨过我、最终彻底根治的三个 Nginx 隐形大坑。
全是血泪经验,全是网上教程不会告诉你的细节。
一、最坑的玄学:本地全正常,代理后 Header 自定义请求头直接消失
这是我踩的第一个大坑,也是绝大多数人入门部署都会卡的一关。
当时我在写自己的用户中心系统,前端把 X-Auth-Token 带上,本地调试从头到尾一切正常,鉴权、登录、权限校验全部稳稳当当。
结果一上 Nginx,直接寄。
后端永远拿不到 Token,所有接口全部报未登录。
我当时整个人都是懵的。
我反复抓包,前端请求里 Header 清清楚楚带着 Token,长度、内容完全没问题。 我去翻 Nginx 日志,零报错、零拒绝、请求正常转发。 后端日志就很直白:请求头为空,鉴权失败。
那一刻我真的怀疑人生:数据明明发出去了,中间经过一个 Nginx,怎么就凭空消失了?
我疯狂换配置、换请求方式、换 Header 名字,折腾了整整一下午。
最后才查到那个90% 新手完全不知道的隐藏规则:
Nginx 默认会直接丢弃带下划线的自定义请求头
我之前为了好记,自定义请求头习惯性写: auth_token、user_session、client_ip
只要名字带下划线,Nginx 默认直接扔掉,不转发给后端,没有任何提示。
这就是所有玄学的根源。
更离谱的是,它不是报错、不是拦截请求、不是拒绝访问,它就是悄悄删掉你的字段,让你业务莫名其妙失效。
除了下划线 Header 被屏蔽之外,我还踩了一连串连锁坑:
默认模板不会透传真实 IP,所有用户请求全部变成本机地址,导致我的登录风控、日志统计全部废掉。 默认不带真实 Host,导致 SSO 回调域名校验失败,第三方登录永远对接不上。 默认不保留原始协议,HTTPS 站点代理后,后端识别成 HTTP,导致跳转、授权全部错乱。
回头看看网上那些千篇一律的极简代理模板,真的害人不浅。
# 千万别再用这种残废模板!!!
location / {
proxy_pass http://127.0.0.1:3000;
}
这种配置只能做到能打开页面,完全撑不起任何真实业务。
折腾完所有坑之后,我总结出自己一直在用的完整版稳定代理配置,彻底根治 Header 丢失问题:
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;
underscores_in_headers on;
proxy_cache_bypass $http_upgrade;
proxy_no_cache $http_upgrade;
}
这里我真心建议所有自建服务的朋友: 永远不要省略这几行 Header 透传配置。
同时我也改掉了自己的命名习惯,现在所有自定义请求头统一用中划线,比如 X-Auth-Token。 不是下划线不能用,而是在 OpenResty 和复杂代理链路中,中划线的兼容性是满分,不会出现任何诡异读取为空的问题。
这是我踩坑踩出来的工程习惯,稳定、省心、不出玄学。
二、最折磨人的长连接坑:WebSocket 准时断线,AI 流式对话根本用不了
如果说 Header 丢失是新手入门坑,那 WebSocket 代理超时,就是进阶开发者的噩梦。
我部署 OpenWebUI、做实时消息推送、搭建流式输出服务的时候,这个问题直接把我搞破防。
本地 WebSocket 连接稳如磐石,挂一天都不会断。 一经过 Nginx 代理,固定 60 秒准时掉线。
我前端写了完整的心跳重连,看着前端心跳包正常发送,后端就是收不到,连接悄无声息被杀死。
没有断开提示、没有错误码、没有日志告警。 就是安安静静断连,用户稍微停顿一秒,对话直接中断,AI 输出直接卡死。
我一开始以为是代码问题,疯狂排查前端重连逻辑、后端超时配置,改了无数版,毫无改善。
后来才彻底搞懂:WebSocket 和 Nginx 默认的 HTTP 短连接逻辑,天生冲突。
很多人不知道一个核心事实: Nginx 默认所有连接,全部按照HTTP 短连接处理。
默认配置下:
- 连接 60 秒无数据,直接强制销毁
- 默认 Connection 为 close,不支持长连接保活
- 不识别 WebSocket 协议升级请求
WebSocket 本质是HTTP 握手、TCP 长连接常驻的特殊协议。 你用普通 HTTP 代理模板去跑长连接,等于戴着枷锁跑步,能稳定才怪。
网上很多教程只教你加两行 Upgrade 配置:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
但90% 的教程都漏改超时时间,这也是绝大多数人依旧疯狂断线的核心原因。
哪怕你协议升级配置写对了,只要超时时间还是默认 60 秒,依旧准时断连。
我测试过无数次,最终打磨出零断线、最稳的 WebSocket 专属配置:
location /ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
把超时直接拉满到 7 天,彻底杜绝 Nginx 层面的主动断连。
改完这一刻,我终于体会到什么叫丝滑稳定。 AI 流式输出、实时推送、长连接驻留,再也没有出现过无故断线。
这里说一句我的真实感悟: 很多技术问题,根本不是代码写得不对,是你不了解中间件的默认限制。
你以为是业务 Bug,其实是基础设施的隐性规则在制裁你。
用 OpenResty 久了之后,我更能体会这一点。 普通 Nginx 只能被动填坑,而 OpenResty 可以通过 Lua 脚本主动管控连接、心跳、拦截、统计,这也是现在实时服务、AI 服务都偏向 OpenResty 的原因。
三、最隐蔽的性能坑:缓存乱生效,改代码永远不更新
这个坑,是我后期优化项目时,最意外的发现。
之前一直疑惑一个问题: 为什么我代码明明更新了,服务器重启了,浏览器刷新无数次,页面还是旧版本?
必须强制清空缓存、无痕模式打开,才能看到新内容。
一开始我以为是前端打包缓存、浏览器缓存问题,折腾了很久,最后定位到:是 Nginx 悄悄帮我缓存了动态接口和页面。
Nginx 的缓存机制真的很 “自作聪明”。
默认情况下,它会主动缓存一些静态资源,而很多人在配置的时候,没有区分静态资源和动态接口。
导致的结果就是:
- 带用户信息的动态页面被缓存,所有人看到同一个页面
- 登录用户和未登录用户页面错乱
- 后端更新数据,前端永远读旧缓存
- WebSocket 握手请求被缓存,导致连接异常
这是非常致命的隐形业务 Bug,但是因为不报错,极难被发现。
我现在的处理逻辑非常清晰,也是我踩坑总结出的最优方案: 静态资源大胆缓存,动态请求一律禁止缓存。
# 静态资源单独长效缓存,提速减压
location ~* \.(jpg|png|gif|css|js|ico|svg)$ {
proxy_pass http://127.0.0.1:3000;
expires 7d;
add_header Cache-Control "public, max-age=604800";
}
# 动态接口、登录请求、长连接请求全部禁用缓存
location / {
proxy_pass http://127.0.0.1:3000;
proxy_cache_bypass $http_upgrade;
proxy_no_cache $http_upgrade;
proxy_cache_bypass $http_cookie $http_authorization;
proxy_no_cache $http_cookie $http_authorization;
}
只要请求带 Cookie、带授权 Token、是协议升级请求,一律不缓存。
这一行配置,直接解决了我过去所有的缓存玄学问题。
四、折腾半年,我对 Nginx 最大的感悟
以前我总觉得,Nginx 就是个转发工具,会抄配置就够了。
踩完这一堆坑之后,我才真正明白: Nginx 是整个服务流量的守门员,它的每一个默认配置,都在悄悄影响你的业务稳定性。
很多开发者的差距,不在于谁会写更多新功能。 而在于谁能避开更多隐形坑、谁的服务更稳、谁更少出现玄学问题。
那些网上看起来简简单单的反向代理配置,背后藏着太多普通人不知道的细节。
Header 过滤规则、长短连接差异、缓存策略、协议升级机制、超时销毁逻辑。 每一个细节,都足以让你的业务随机翻车。
我今天写的这三个问题,覆盖了 90% 自建服务的高频 Bug:
- 请求头丢失导致的登录、鉴权玄学
- WebSocket 超时导致的实时服务断线
- 缓存乱生效导致的数据错乱、版本不更新
这是我实打实踩坑、复盘、优化、沉淀出来的经验。
现在我再搭建任何服务、部署任何项目,都会直接套用这套稳定配置。 再也没有出现过莫名其妙、找不到原因的玄学问题。
技术路上最磨人的,从来不是复杂的源码, 而是这些看似简单、实则暗藏规则的隐形陷阱。
看懂了这些,你就从 “只会抄配置” 的使用者, 真正变成了懂原理、会避坑、能稳住服务的开发者。











暂无评论内容