更新于

Nginx 502 Bad Gateway 怎么解决:按 error.log 找到上游原因,在 Ubuntu 上一步步修好

简短回答:Nginx 返回 502 Bad Gateway,是因为它转发到的上游(你的应用、PHP-FPM、Gunicorn、容器)拒绝了连接、没回响应就断开,或者回了 Nginx 不接受的内容。Nginx 本身通常没坏。请求失败后马上执行 sudo tail -n 50 /var/log/nginx/error.log:那一刻写下的报错会指出原因,upstream: 字段就是 Nginx 实际去连的地址。

本文写给在 Ubuntu 22.04 / 24.04 上用 Nginx 做反向代理的开发者。先给一张 Nginx 真实报错对照表,再按顺序走一遍 502 bad gateway 的解决步骤。排查步骤里的命令只查看状态,会改动服务器的操作统一放在最后。只在你有权限的服务器上执行。

502 bad gateway nginx 是什么意思

Nginx 就是那个 “gateway”(网关)。502 表示 Nginx 收下了访问者的请求、转给了后面的服务,却没拿到能用的响应。常见原因:

如果应用有响应、只是太慢,Nginx 返回的是 504 Gateway Timeout。先分清 502 还是 504,排查范围就小了一半。

对照 error.log 报错原文

找到和失败请求同一时间的那一行。第一列是 Nginx 实际写出的原文;你服务器上的地址、socket 路径、PHP 版本号会不一样。每行末尾还有 client: ..., server: ..., request: "...", upstream: "...", host: "..." 这些上下文。

error.log 里写的状态码可能原因下一步
connect() failed (111: Connection refused) while connecting to upstream502upstream: 地址上没有程序在监听:应用停了、崩了、还在启动,或者换了端口第 4 步、第 5 步
connect() to unix:/run/php/php8.3-fpm.sock failed (2: No such file or directory) while connecting to upstream502socket 文件不存在:PHP-FPM(或应用)没在跑,或者 Nginx 指向了另一个版本的 socketls -l /run/php/;第 5 步
connect() to unix:/run/php/php8.3-fpm.sock failed (13: Permission denied) while connecting to upstream502Nginx 的运行用户(Ubuntu 上是 www-data)打不开这个 socket第 7 步
connect() to unix:/run/php/php8.3-fpm.sock failed (11: Resource temporarily unavailable) while connecting to upstream502socket 的等待队列满了:worker 全忙,排队也排满了查 PHP-FPM 日志里的 max_children;第 8 步
upstream prematurely closed connection while reading response header from upstream502应用接了请求,没回响应就断开:崩溃、worker 被杀、应用服务器自己超时第 8 步
recv() failed (104: Connection reset by peer) while reading response header from upstream502上游重置了连接:进程崩溃或重启、worker 被终止,或者复用的长连接已被对方关掉第 8 步;长连接
upstream prematurely closed FastCGI stdout while reading response header from upstream502PHP-FPM 的 worker 还没输出就结束了(致命错误、崩溃或被杀)PHP-FPM 日志和 PHP 错误日志
upstream sent invalid header: "..." while reading response header from upstream502应用发来的某行响应头格式不对无效响应
upstream sent too big header while reading response header from upstream502响应头(常见是很大的 Set-Cookie)超出了 proxy_buffer_size(PHP 是 fastcgi_buffer_size)缓冲区
SSL_do_handshake() failed (SSL: error:...:wrong version number) while SSL handshaking to upstream502proxy_pass https:// 指向了一个只讲明文 HTTP 的端口这个上游改用 http://,或者指向它的 TLS 端口
no live upstreams while connecting to upstream502upstream {} 里所有服务器都因为之前的错误被标成不可用往上翻:更早的报错才说明每台为什么失败
connect() failed (113: No route to host) while connecting to upstream502上游 IP 不可达:IP 写错、主机或容器已不在,或被防火墙拒绝ip route get UPSTREAM_IP;Docker
upstream timed out (110: Connection timed out) while reading response header from upstream504应用在 proxy_read_timeout(默认 60 秒)内没回应看应用日志里的慢请求;超时

两个看着像、其实不是 502 的:

Nginx 写的是 invalid header;“The proxy server received an invalid response from an upstream server” 是 Apache 的原文,不是 Nginx

Nginx 不会写 “upstream sent invalid response” 这句原话。上游回的内容有问题时,错误日志里是下面几种之一,最终都返回 502:

想看应用到底回了什么,绕过 Nginx 直接看原始响应头(第 6 步):

curl -sS -o /dev/null -D - --max-time 10 -H 'Host: example.com' http://127.0.0.1:3000/PATH

如果你看到的是 “The proxy server received an invalid response from an upstream server”,那是 Apache mod_proxy 的错误页,不是 Nginx 的,去看 Apache 的错误日志(Ubuntu 上是 /var/log/apache2/error.log)。

502 Bad Gateway 解决步骤(Ubuntu)

把 example.com、YOUR_APP、端口 3000 和 socket 路径换成你自己的。Ubuntu 22.04 自带 PHP 8.1,24.04 自带 PHP 8.3,php8.3-fpm 按实际版本改。

第 1 步:确认 502 是你自己的 Nginx 返回的

curl -sS -o /dev/null -D - https://example.com/ | grep -i -E '^(HTTP|server|cf-ray)'
curl -sS -o /dev/null -w '%{http_code}\n' --resolve example.com:443:127.0.0.1 https://example.com/

第一条走 DNS 和 CDN。响应头里有 server: cloudflare 或 cf-ray,说明是 CDN 回的;CDN 的 502 页面表示它没从你的服务器拿到正常响应。第二条在服务器上执行,把同样的请求直接发给本机 Nginx、跳过 CDN(站点没有 TLS 就用 http:// 和 80 端口)。这条也是 502,就继续往下查;这条是 200,问题出在 CDN 和你服务器之间,不在 Nginx 后面。

Nginx 自己的错误页显示 502 Bad Gateway,底部有一行类似 nginx/1.24.0 (Ubuntu)。那只是版本号,升级 Nginx 解决不了 502。

第 2 步:确认 Nginx 在运行、配置没错

systemctl status nginx --no-pager
sudo nginx -t

看到 active (running),加上 syntax is ok 和 test is successful,说明 Nginx 本身是好的,502 出在上游。如果 nginx -t 报错,正在跑的 Nginx 可能还在用旧配置;先修好它指出的错误,再考虑重载。

第 3 步:看 502 发生那一刻的错误日志

sudo tail -n 50 /var/log/nginx/error.log
sudo grep -E 'upstream|connect\(\)' /var/log/nginx/error.log | tail -n 20
sudo awk '$9 == 502' /var/log/nginx/access.log | tail -n 5

某个 server 块可能有自己的日志文件。找出它,以及每个 location 转发到哪里:

sudo nginx -T 2>/dev/null | grep -n -E 'error_log|access_log|proxy_pass|fastcgi_pass|uwsgi_pass|upstream'

nginx -T 会打印完整的已加载配置,里面可能有凭证,不要贴到公开渠道。502 时有时无的话,在一个终端里开着 sudo tail -f /var/log/nginx/error.log,在另一个终端里复现请求。

完整的一行长这样:

2026/10/08 14:03:12 [error] 1234#1234: *5678 connect() failed (111: Connection refused) while connecting to upstream, client: 203.0.113.10, server: example.com, request: "GET /api/orders HTTP/1.1", upstream: "http://127.0.0.1:3000/api/orders", host: "example.com"

第 4 步:看上游服务的状态和日志

systemd 管理的应用(Node、Gunicorn、uWSGI、Puma 等):

systemctl status YOUR_APP --no-pager
sudo journalctl -u YOUR_APP --since "30 min ago" --no-pager | tail -n 50

PHP-FPM:

systemctl status php8.3-fpm --no-pager
sudo journalctl -u php8.3-fpm --since "30 min ago" --no-pager | tail -n 30
sudo tail -n 50 /var/log/php8.3-fpm.log

容器:

docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
docker logs --since 30m --timestamps YOUR_CONTAINER 2>&1 | tail -n 50

看服务是不是停了、在重启循环里(activating (auto-restart)、Restarting (1)),或者在 502 那个时间点记录了崩溃。容器反复退出的话,另有一篇:Docker 退出码是什么意思?容器秒退、立即退出时看 0、1、125、137、139、143、255。

第 5 步:确认 Nginx 去连的地方有程序在监听

sudo ss -ltnp
sudo ss -lxp | grep -E 'php|gunicorn|uwsgi|\.sock'
ls -l /run/php/

ss -ltnp 列出正在监听的 TCP 端口和占用它的进程,ss -lxp 列出 Unix socket。拿它们和第 3 步的 upstream: 对比:

第 6 步:绕过 Nginx,直接请求上游

curl -sS -o /dev/null -w '%{http_code} %{time_total}s\n' --max-time 10 -H 'Host: example.com' http://127.0.0.1:3000/
curl -sS -o /dev/null -w '%{http_code}\n' --max-time 10 --unix-socket /run/gunicorn.sock http://localhost/

路径选一个可以安全请求的。第二条用于挂在 Unix socket 上的 HTTP 应用(Gunicorn、Puma、HTTP 模式的 uWSGI)。

PHP-FPM 说的是 FastCGI,不是 HTTP,curl 测不了。如果 pool 配置里开了 ping.path,可以用 libfcgi-bin 包里的 cgi-fcgi 请求它:

SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.3-fpm.sock

没开的话,服务在运行、www-data 能打开 socket 文件(第 7 步),就是实用的判断标准。

第 7 步:检查 Unix socket 权限

Ubuntu 上 Nginx 的 worker 以 www-data 运行。它需要对 socket 有写权限,并且对 socket 上面每一级目录都有执行(进入)权限。

ps -o user= -C nginx | sort | uniq -c
ls -l /run/php/php8.3-fpm.sock
sudo grep -E '^\s*(user|group|listen(\.owner|\.group|\.mode)?)\s*=' /etc/php/8.3/fpm/pool.d/*.conf
namei -l /run/gunicorn.sock

第 8 步:查应用是不是被杀了或者超时了

upstream prematurely closed connection 和 Connection reset by peer,通常说明处理这个请求的 worker 没了。在 502 发生的同一分钟里查:

sudo journalctl -k --since "1 hour ago" --no-pager | grep -i -E 'out of memory|killed process'
sudo journalctl -u YOUR_APP --since "1 hour ago" --no-pager | grep -i -E 'worker timeout|killed|signal|error'
sudo grep -i -E 'timed out|max_children|exited on signal' /var/log/php8.3-fpm.log | tail -n 20

Docker 里,127.0.0.1 不是你的应用

在 Nginx 容器里写 proxy_pass http://127.0.0.1:3000;,指向的是 Nginx 容器自己,结果就是 (111: Connection refused)。常见改法如下,每一项都是变更,先评估再做:

Nginx 在加载配置时解析 proxy_pass 里的主机名。应用容器重建后换了 IP,Nginx 可能还在连旧 IP,日志里是 (113: No route to host) 或 (111: Connection refused),直到重载为止。官方 Nginx 镜像把错误日志输出到 stderr:docker logs --since 15m YOUR_NGINX_CONTAINER 2>&1 | grep -i upstream。

超时、缓冲区和长连接:日志指向这里时再改

超时对应的是 504,不是 502。 proxy_connect_timeout、proxy_send_timeout、proxy_read_timeout 默认都是 60 秒。只给本来就慢的那个 location(比如导出报表)调大 proxy_read_timeout,并且先弄清它为什么慢。

upstream sent too big header 要调大缓冲区。 proxy_buffer_size 默认是一个内存页(x86_64 上是 4k)。和 proxy_buffers 一起调:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffer_size 16k;
    proxy_buffers 8 16k;
}

只把 proxy_buffer_size 调到 16k,nginx -t 会报 "proxy_busy_buffers_size" must be less than the size of all "proxy_buffers" minus one buffer,因为 busy buffers 的默认值会跟着变大。PHP-FPM 对应的是 fastcgi_buffer_size 和 fastcgi_buffers。响应头一直在变大的话,那个 Cookie 或响应头本身也要修。

用了上游长连接,偶尔出现 502。 如果 upstream {} 里配了 keepalive,Nginx 会复用空闲连接。应用比 Nginx 更早关掉空闲连接时,请求可能正好落在一条刚被关掉的连接上,Nginx 就记 upstream prematurely closed connection 或 (104: Connection reset by peer)。比如 Node.js 默认 5 秒后关闭空闲的 keep-alive 连接(server.keepAliveTimeout)。让应用的空闲超时长于 upstream 块里的 keepalive_timeout(默认 60 秒),或者把后者调小。

安全地动手修复

拉起停掉的应用、改 proxy_pass 或 socket 路径、修 socket 属主、调缓冲区,都属于变更:

  1. 先备份文件,比如 sudo cp /etc/nginx/sites-available/example.com /root/example.com.bak。
  2. 只改日志指向的那一处。
  3. sudo nginx -t,通过后 sudo systemctl reload nginx(reload 不断开现有连接,restart 会断开)。
  4. 用原来失败的请求再试一次,同时看着 sudo tail -f /var/log/nginx/error.log。
  5. 没有好转就把备份放回去,再 nginx -t、重载。

不要为了保险把所有超时都调大,也不要把 socket 权限放开到 0666;日志指向哪个原因,就修哪个。

说明性示例

说明性示例,不是真实客户案例:服务器从 Ubuntu 22.04 升级到 24.04(PHP 8.1 变成 8.3)后,所有 PHP 页面都返回 502。错误日志里是 connect() to unix:/run/php/php8.1-fpm.sock failed (2: No such file or directory) while connecting to upstream。ls -l /run/php/ 只有 php8.3-fpm.sock,systemctl status php8.1-fpm 提示找不到这个服务。这支持“站点配置里还是旧 socket 路径”的判断。怎么修由人来定:确认站点需要的 PHP 扩展在 8.3 下都装好了,再把 fastcgi_pass 改成新 socket,nginx -t、重载、再测一次。

AI 排查提示词

这个网站返回 502 Bad Gateway。请先只做检查、不做任何改动:找到正在使用的 Nginx error_log,列出最近 15 分钟和 upstream 有关的报错,把 upstream: 地址和实际监听情况(端口、Unix socket、容器)对比,查看上游服务的状态和日志,并在服务器上直接请求上游。区分已证实的事实和推测。不要重启服务或修改配置;如需变更,先给出最小步骤、风险、验证方法和回退方案,等我同意。

参考:Nginx proxy 模块、Nginx upstream 模块、Nginx fastcgi 模块。

如果整站是半夜才打不开,502 可能只是其中一个症状,先看 网站半夜打不开 时怎么做前 15 分钟分流。如果同一时间还有 可疑 SSH 登录,先做只读排查并保留现场,不要为了消掉 502 去删日志或重启。

相关指南

常见问题

Nginx 报 502 Bad Gateway 是什么原因,怎么解决?

502 表示 Nginx 收下了请求、转给上游(你的应用、PHP-FPM、Gunicorn 或容器),却没拿到能用的响应:上游拒绝了连接、没回响应就断开,或者回了 Nginx 不接受的内容。Nginx 本身通常没坏。请求失败后马上执行 sudo tail -n 50 /var/log/nginx/error.log,那一刻写下的报错会指出原因,upstream: 字段就是 Nginx 实际去连的地址。

「The proxy server received an invalid response from an upstream server」是 Nginx 的报错吗?

不是。这句是 Apache mod_proxy 的错误页,要去看 Apache 的错误日志(Ubuntu 上是 /var/log/apache2/error.log)。Nginx 也不会写「upstream sent invalid response」这句原话:上游回的内容有问题时,error.log 里是 upstream sent invalid header、upstream sent too big header 或和 Content-Length 有关的报错,最终都返回 502。

error.log 里是 connect() failed (111: Connection refused) while connecting to upstream,怎么办?

这表示 upstream: 那个地址上没有程序在监听:应用停了、崩了、还在启动,或者换了端口。先用 systemctl status 或 docker ps -a 看上游服务的状态和日志,再用 sudo ss -ltnp 把实际监听的端口和 upstream: 对一遍。Nginx 跑在容器里时,proxy_pass http://127.0.0.1:3000 指向的是 Nginx 容器自己;在同一个 Compose 网络里,常见改法是转发到服务名。

502 和 504 有什么区别?502 要不要调大超时?

502 是上游拒绝连接、没回响应就断开,或者回了无效内容;504 Gateway Timeout 是应用有响应但太慢,没在 proxy_read_timeout(默认 60 秒)内回应。超时对应的是 504,所以 502 时调大 Nginx 超时一般没用;比如 Gunicorn 的 WORKER TIMEOUT,是应用服务器先把请求掐了。只给本来就慢的 location 调大超时,并先弄清它为什么慢。

需要帮助理解排查结果?

OpsMate(itops.sh)把 SSH 终端和 AI 助手放在同一页:上面的命令可以自己手敲,也可以用一句话描述 502,再拿 AI 的回答和原始输出对照。发给 AI 的命令输出会在云端处理,请先去掉密钥等敏感信息。免费版每月 500 次 AI 调用,服务器数量不限。

免费开始 本地凭证桌面端

排障指南