Nginx 502 Bad Gateway 排查全链路:从日志、连接层到上游服务的实战指南
发布时间:2026/10/16 6:34:14 锦皓数字建站

1. 从一次线上告警说起502到底意味着什么凌晨两点手机开始疯狂震动。监控平台推送过来一条告警核心网关的5xx错误率在五分钟内从0.02%飙升到37%。打开大盘一看清一色的502 Bad Gateway。这个状态码对运维和后端同学来说再熟悉不过了但它背后的成因却可能横跨网络、系统、应用、配置四个层面排查起来非常考验基本功。先把概念说清楚。502 Bad Gateway的本质是网关或代理服务器这里就是Nginx作为中间人向上游服务器upstream发起请求后收到了一个它认为无效的响应或者压根没收到响应。注意是Nginx认为无效这个判断标准很关键。它可能是上游直接拒绝连接可能是上游返回了格式错误的响应头也可能是上游处理超时后Nginx主动断开。所以502不是一种病而是一类症状就像发烧一样病因可能十几种。这篇文章适合谁看如果你正在维护Nginx网关、负责后端服务的稳定性或者你是个开发同学线上服务偶尔被网关甩个502过来却不知道从哪查起那这篇内容就是写给你的。我会把整个排查过程拆成一条可复用的路径从最外层的日志开始一层层往里剥每一步都告诉你为什么这么查、查到了怎么判断、判断完怎么处理。中间会穿插我自己踩过的坑和一些容易被忽略的细节比如内核参数、文件描述符、DNS解析缓存这些平时不太注意但关键时刻要命的东西。整个排查的核心思路就一句话先定位502是Nginx自己产生的还是上游真实返回的然后再顺着连接链路逐段排查。听起来简单但实际操作中很多人一上来就去看后端日志结果方向完全错了。下面我按真实排查顺序展开你可以直接照着这个流程走。2. 排查前的准备工作先把日志和监控用起来2.1 确认Nginx错误日志的级别和格式很多团队Nginx的error_log级别默认是error这个级别其实已经能记录502相关的信息了但如果你想要更细的上下文比如上游的连接耗时、具体是哪台后端出的问题就需要把日志格式调一下。我习惯在http块里自定义一个log_format把upstream_addr、upstream_status、upstream_response_time、request_time这几个变量都带上。这样一条访问日志就能告诉你请求打到了哪台后端、后端返回了什么状态码、后端处理了多久、Nginx总共花了多久。log_format detailed $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent upstream_addr$upstream_addr upstream_status$upstream_status upstream_response_time$upstream_response_time request_time$request_time;这个格式配好之后排查502的时候直接grep一下访问日志里upstream_status为502或者upstream_addr为空的记录就能快速判断问题出在哪一段。如果upstream_status是502说明后端确实返回了502问题在后端如果upstream_status是-说明Nginx根本没拿到后端的响应问题在连接层。2.2 监控指标要覆盖的三个维度光有日志还不够排查502需要监控提供趋势和分布。我建议至少覆盖三个维度第一是Nginx自身的连接指标包括active connections、reading、writing、waiting这四个值的变化趋势第二是upstream的健康指标每台后端的响应时间和错误率第三是系统层指标包括TCP连接数、TIME_WAIT数量、文件描述符使用率、CPU和内存。这三个维度缺一不可。我遇到过好几次情况是Nginx层看起来正常但系统层的TIME_WAIT数量爆了导致新连接建立缓慢最终表现为间歇性502。如果只看Nginx监控根本发现不了。提示监控采集频率建议至少10秒一次502这种问题往往来得快去得也快采集间隔太长会漏掉关键窗口。2.3 准备一份上游服务清单排查之前先把当前Nginx配置里所有的upstream块整理出来包括每台后端的IP、端口、权重、健康检查配置。这份清单在排查过程中会反复用到尤其是当你需要逐台后端去验证连通性的时候。我一般会直接从nginx.conf里把upstream段提取出来配合配置管理工具生成一份最新的拓扑图避免因为配置变更导致排查时信息过时。3. 第一层排查从Nginx错误日志定位502的直接原因3.1 读懂错误日志里的关键信息Nginx的error_log里关于502的记录通常长这样2024/01/15 02:13:47 [error] 12345#0: *987654 connect() failed (111: Connection refused) while connecting to upstream, client: 10.0.1.100, server: api.example.com, request: GET /v1/order HTTP/1.1, upstream: http://10.0.2.10:8080/v1/order, host: api.example.com这条日志信息量很大。connect() failed说明Nginx在尝试建立到上游的TCP连接时就失败了括号里的111: Connection refused是系统错误码表示目标端口没有进程在监听。upstream字段告诉你是哪台后端request字段告诉你是哪个接口。如果是upstream timed out那就是连接建立了但后端迟迟不返回数据。如果是no live upstreams说明健康检查把所有后端都摘掉了。我一般会先用一条命令把错误日志里的502相关记录按错误类型聚合一下grep -E 502|upstream /var/log/nginx/error.log | \ awk -F[()] {print $2} | sort | uniq -c | sort -rn这样能快速看出是连接拒绝占多数还是超时占多数还是其他类型。不同类型的处理方向完全不同。3.2 区分几种典型的502错误模式根据错误日志502通常可以归为以下几类每类的排查方向不一样错误日志关键词含义排查方向connect() failed (111: Connection refused)上游端口无监听检查后端进程是否存活、端口是否被防火墙拦截upstream timed out上游响应超时检查后端处理耗时、Nginx超时配置no live upstreams所有后端被健康检查摘除检查健康检查配置和后端实际状态upstream sent invalid header上游返回了非法响应头检查后端框架的响应头设置recv() failed (104: Connection reset by peer)上游主动断开连接检查后端是否崩溃、是否触发了OOMworker_connections are not enoughNginx自身连接数不够调整worker_connections和worker_rlimit_nofile这张表建议收藏排查的时候对着错误日志里的关键词直接定位能省很多时间。我见过不少同学一看到502就去看后端日志结果错误日志里明明写着Connection refused后端进程早就挂了看后端日志当然什么都看不到。3.3 用stub_status模块快速看Nginx自身状态如果错误日志里没有明显的上游错误那就要怀疑Nginx自身是不是出了问题。开启stub_status模块后访问一个指定路径就能看到当前连接状态location /nginx_status { stub_status on; allow 127.0.0.1; deny all; }返回的内容里Active connections是当前活跃连接数Reading是正在读取请求头的连接数Writing是正在写响应的连接数Waiting是保持空闲的连接数。如果Waiting特别大而Writing很小说明大量连接卡在等待上游响应问题大概率在上游。如果Reading很大说明请求头读取都卡住了可能是客户端网络问题或者Nginx worker不够用。4. 第二层排查连接层与系统层的问题定位4.1 检查上游端口的连通性确认了错误日志指向连接问题后第一步就是在Nginx所在机器上直接测试到上游的连通性。用telnet或者nc都可以# 测试TCP连接是否可达 nc -zv -w 3 10.0.2.10 8080 # 或者用curl直接请求上游绕过Nginx curl -v --connect-timeout 3 http://10.0.2.10:8080/health如果nc显示Connection refused那基本可以确定是后端进程没起来或者端口没监听。如果nc能通但curl超时说明TCP层通了但应用层没响应可能是后端线程池满了或者卡在某个慢查询上。如果nc直接超时那就要查网络层了比如安全组规则、iptables、路由表。注意测试的时候一定要从Nginx所在的机器发起不要从你自己的办公电脑测。很多网络策略是单向的或者只针对特定源IP放行的从错误的源地址测试会得出完全错误的结论。4.2 系统层的关键指标排查连接层的问题很多时候根子在系统层。我整理了几个必查的系统指标文件描述符限制。Nginx每个连接都要占用一个文件描述符如果worker_rlimit_nofile设置得太小或者系统级的fs.file-max不够新连接就建立不了。用ulimit -n看当前限制用cat /proc/sys/fs/file-nr看系统级使用情况。TCP连接队列。net.core.somaxconn控制accept队列的最大长度如果这个值太小高并发时新连接会被丢弃。net.ipv4.tcp_max_syn_backlog控制SYN队列长度。这两个值在流量突增时特别关键。TIME_WAIT数量。用ss -s或者netstat -n | awk /^tcp/ {S[$NF]} END {for(a in S) print a, S[a]}查看。如果TIME_WAIT数量过大会占用大量本地端口导致Nginx无法建立新的上游连接。可以通过开启net.ipv4.tcp_tw_reuse来缓解但要注意这个参数只对出站连接有效。内存和OOM。用dmesg | grep -i oom检查是否有进程被OOM Killer干掉了。后端进程如果因为内存不足被杀Nginx这边就会看到Connection refused。4.3 一个真实的排查案例之前遇到过一次502错误日志里全是connect() failed (111: Connection refused)但后端进程明明活着。后来发现是后端服务的监听队列满了新连接被内核拒绝。具体原因是后端的accept线程处理太慢导致SYN队列溢出。用netstat -s | grep -i listen看到大量的listen queue overflow计数。解决办法是调大net.core.somaxconn和后端框架的backlog参数同时优化accept线程的处理逻辑。这个案例说明一个问题Connection refused不一定代表进程挂了也可能是队列满了。所以看到这个错误码先确认进程状态再确认队列情况最后才下结论。5. 第三层排查Nginx配置与超时参数的深度检查5.1 超时参数之间的联动关系Nginx和上游之间的超时涉及好几个参数它们之间有优先级和联动关系配错了很容易导致502。核心参数有这几个proxy_connect_timeoutNginx与上游建立TCP连接的超时时间默认60秒。proxy_send_timeoutNginx向后端发送请求的超时时间默认60秒。proxy_read_timeoutNginx等待后端返回响应的超时时间默认60秒。proxy_next_upstream_timeout重试下一个上游的总超时时间。proxy_next_upstream_tries重试次数。这几个参数的关系是如果proxy_read_timeout设得太短后端处理时间稍微长一点就会触发超时Nginx会断开连接并返回502。如果设得太长后端真的挂了的时候Nginx会一直等占用连接资源。我一般会根据接口的P99响应时间来设置通常是P99的2到3倍。5.2 proxy_next_upstream的配置陷阱proxy_next_upstream决定了在什么情况下Nginx会把请求转发到下一台后端。默认值是error timeout意思是连接错误和超时才重试。但很多人不知道的是如果配置了proxy_next_upstream http_502那么当一台后端返回502时Nginx会自动重试下一台。这个配置在有多台后端的时候非常有用但要注意它只对幂等请求安全。proxy_next_upstream error timeout http_502 http_503 http_504; proxy_next_upstream_tries 3; proxy_next_upstream_timeout 10s;这里有个坑如果所有后端都返回502Nginx重试完所有后端后最终返回给客户端的还是502。所以这个配置只能缓解单台后端故障不能解决全局性问题。另外proxy_next_upstream_tries设得太大在全局故障时会放大请求量形成雪崩。5.3 缓冲区配置不当导致的502proxy_buffer_size和proxy_buffers这两个参数控制Nginx读取上游响应的缓冲区大小。如果上游返回的响应头超过了proxy_buffer_sizeNginx会报upstream sent too big header错误然后返回502。这个错误在错误日志里很明确但容易被忽略。proxy_buffer_size 16k; proxy_buffers 8 16k; proxy_busy_buffers_size 32k;我一般会把proxy_buffer_size设成16k对于大多数场景够用了。如果后端返回的Cookie或者自定义头特别大可能需要调到32k甚至64k。这个值不是越大越好每个连接都会分配这些缓冲区设太大在并发高的时候会吃光内存。6. 第四层排查上游服务自身的问题定位6.1 后端日志与Nginx日志的时间对齐如果Nginx错误日志显示的是upstream timed out或者recv() failed那问题基本可以确定在后端。这时候需要把Nginx的访问日志和后端的应用日志按时间对齐来看。关键是对比同一个请求在两边的时间戳和耗时。我通常会用request_id来做关联。在Nginx里生成一个唯一的request_id通过请求头传给后端后端在日志里也记录这个ID。这样排查的时候直接grep这个ID就能把整个链路的日志串起来。没有request_id的话只能靠时间戳和接口路径去猜效率低很多。6.2 后端常见的502诱因后端导致502的原因五花八门我按出现频率排个序进程崩溃或重启。这是最常见的。后端进程因为未捕获的异常、内存溢出、死锁等原因挂掉Nginx这边立刻就是Connection refused。排查方法是看后端进程的存活监控和重启记录。线程池或连接池耗尽。后端处理请求的线程池满了新请求排队等待超过Nginx的proxy_read_timeout后就变成502。这种情况后端日志里通常没有错误因为请求根本没被处理。需要看线程池的活跃数和队列长度。慢查询或外部依赖超时。后端在处理请求时调用数据库或者第三方接口对方响应慢导致整个请求处理时间超过Nginx的超时设置。这种502的特点是间歇性的跟特定接口相关。GC停顿。对于Java类应用Full GC会导致所有线程停顿停顿期间无法响应任何请求。如果GC停顿时间超过Nginx超时就会502。需要看GC日志和停顿时间监控。文件描述符耗尽。后端进程打开的文件或连接数达到上限无法接受新连接。用lsof -p pid | wc -l查看。6.3 用压测复现和验证如果502是间歇性的很难抓到现场可以考虑在低峰期用压测工具复现。用wrk或者ab对特定接口施压同时观察Nginx错误日志和后端监控。压测的时候要逐步增加并发找到触发502的临界点然后分析那个临界点对应的系统指标变化。提示压测一定要在预发布环境或者低峰期做并且提前通知相关同学。我见过有人在生产环境直接压测结果把整个服务打挂了502变成了全面不可用。7. 常见问题速查表与避坑经验7.1 502排查速查表现象可能原因快速验证方法处理方向错误日志大量Connection refused后端进程挂掉或端口未监听nc -zv测试端口重启后端、检查进程监控错误日志大量upstream timed out后端处理慢或超时配置过短对比后端P99耗时和proxy_read_timeout优化后端或调大超时错误日志no live upstreams健康检查失败摘除所有后端查看健康检查日志修复后端健康状态、调整检查策略错误日志upstream sent too big header响应头超过缓冲区检查后端返回的响应头大小调大proxy_buffer_size错误日志worker_connections are not enoughNginx连接数不够查看stub_status的Active connections调大worker_connections和nofile无错误日志但客户端收到502可能是上游返回的502查看访问日志的upstream_status排查后端应用逻辑间歇性502且与特定接口相关后端慢查询或外部依赖超时对比接口耗时监控优化慢查询、增加熔断流量突增时大量502系统层队列溢出或资源不足查看somaxconn和TIME_WAIT调内核参数、扩容7.2 几个容易踩的坑坑一只看Nginx日志不看系统日志。有一次502排查了半天Nginx错误日志里只有零星的timeout最后发现是系统内存不足触发了OOM Killer把后端进程杀了。dmesg里清清楚楚记录着但一开始没人想到去看。坑二忽略DNS解析问题。如果upstream里配置的是域名而不是IPNginx会在启动时解析一次然后缓存。如果后端IP变了但Nginx没重载就会一直往旧IP发请求导致502。解决办法是配置resolver并设置合理的valid时间或者直接用IP。坑三健康检查配置过于激进。健康检查间隔太短、失败阈值太低后端稍微抖动一下就被摘除导致所有后端都被摘掉Nginx返回no live upstreams。我一般会把失败阈值设成3次间隔5秒给后端足够的恢复时间。坑四proxy_next_upstream重试放大了故障。全局故障时每个请求都重试3次请求量变成原来的3倍把本来还有一口气的后端彻底打挂。所以重试次数要谨慎设置并且配合熔断机制。坑五忘了检查Nginx的worker进程数。worker_processes设成auto通常没问题但如果设成了固定值且小于CPU核数高并发时worker不够用连接处理不过来。用nproc确认核数确保worker_processes匹配。7.3 我个人的排查习惯我排查502一般遵循从外到内、从粗到细的顺序先看监控大盘确认影响范围和时间窗口再看Nginx错误日志定位错误类型然后根据错误类型决定是查连接层还是应用层最后用具体命令验证假设。整个过程控制在15分钟内如果15分钟还没定位到根因就先做止损操作比如重启后端、扩容、切流量然后再慢慢分析。止损和排查要并行不要为了找到根因让服务一直挂着。我见过有同学非要找到根因才肯处理结果502持续了半个小时影响面扩大了好几倍。先恢复服务再复盘根因这个顺序不能反。另外每次排查完我都会把过程和结论记录到故障库里包括错误日志片段、关键命令、根因、处理措施。下次遇到类似问题直接搜故障库就能找到参考效率高很多。这个习惯坚持了几年现在团队里新同学遇到502先查故障库大部分常见问题都能自己解决。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。