Nginx 504 Gateway Timeout 排查实战

线上接口偶发或直接大面积返回 Nginx 504 Gateway Timeout,和 502 一样让人头大,但根因恰恰相反:Nginx 504 表示“网关已经连上上游,但上游在限定时间内没能返回响应”。本文从 error.log 出发,逐层剖析 Nginx 504 的五大根因——慢查询、超时配置、反代链过长、后端阻塞、大响应——并给出分级超时与异步化的根治方案。

一、504 与 502 的本质区别

502 是“连不上 / 被拒”,504 是“连上了但太慢”。二者排查路径完全不同(对比见《Nginx 502 排查实战》)。Nginx 作为反向代理(见《Nginx 反向代理完整配置》),对上游的每一次转发都有超时上限,超过就返回 504。

二、第一步:从 error.log 确认是超时

先读日志,关键字直接区分 502 与 504:

tail -f /var/log/nginx/error.log

# Nginx 504 的典型关键字
# upstream timed out (110: Connection timed out)        -> 读超时
# upstream timed out ... while reading response header  -> 后端处理太久
# 注意:它不是 502 的 "Connection refused",而是 "timed out"

三、根因一:后端真的慢(慢 SQL / 慢接口)

最常见。用 curl 复现并计时,确认是后端耗时:

curl -o /dev/null -w "time_total=%{time_total}s\n" https://api.example.com/slow

# 若数据库查询慢,从索引与 SQL 入手
# PostgreSQL:见《PostgreSQL 慢查询优化》
# MySQL:见《MySQL 索引底层原理》
# Java 服务 GC 停顿:见《Java 内存泄漏排查复盘》

四、根因二:超时配置过短

默认 proxy_read_timeout 为 60s,但链路深处常常不够。按业务 P99 放宽:

location /api/ {
    proxy_pass http://backend;
    proxy_connect_timeout 5s;
    proxy_read_timeout    120s;   # 按真实 P99 调整,而非无脑拉满
    proxy_send_timeout    120s;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

PHP-FPM 同步改 fastcgi_read_timeout;Spring Boot / 网关侧也要对应用户的读取超时,避免“上游已断、下游还在等”的错配。

五、根因三:反代链过长,超时叠加

当链路是 Nginx → Nginx → 后端 时,外层超时必须大于内层之和,否则内层还在算、外层已断:

# 边缘 Nginx(面向用户)
proxy_read_timeout 120s;
# 内层 Nginx(转发到后端)
proxy_read_timeout 90s;
# 后端处理应 <= 60s,否则继续下钻后端

六、根因四:后端被阻塞(锁 / 同步 / 事件循环)

连接池满、慢锁、同步调用第三方都会让线程卡住,表现为请求排队超时。本质与 MySQL 连接数满同类(见《MySQL 连接数满排查实战》);文件描述符耗尽也会表现为超时(见《文件描述符耗尽排查》):

ss -s                          # 看总 TCP 连接与端口分配,是否堆积
jstack <java_pid> | grep -A20 "BLOCKED"   # Java 看线程栈锁竞争
netstat -an | grep :8080 | wc -l           # 活跃连接是否逼近上限

七、根因五:大文件 / 大响应传输慢

导出大报表、大附件会拖慢首字节。优先用流式响应、分页、异步生成后回调,或走对象存储直链;Docker 部署形态见《Docker 编排实战》。

八、排查清单(速查表)

error.log 关键字最可能根因优先动作
upstream timed out读超时调 proxy_read_timeout、查慢接口
timed out (while reading response header)后端处理太久查慢 SQL / GC / 锁
connect timeout连不上后端查网络 / 安全组 / 端口
494 / 循环超时反代链超时叠加对齐各层超时

九、治本:分级超时 + 熔断 + 异步化 + 监控

临时放宽不算完。给 upstream 配熔断、把长任务异步化(消息队列)、在《GitHub Actions CI》里加超时断言,并用《Uptime Kuma 自建监控》盯 P95 响应。把排障脚本沉淀成《VS Code 代码片段》,下次定位更快:

upstream backend {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
}

location /api/ {
    proxy_pass http://backend;
    proxy_read_timeout 120s;
    proxy_next_upstream error timeout http_504;
}

# 异步探活:长任务改为先返回任务号,轮询结果
curl -X POST https://api.example.com/jobs -d '{"task":"export"}'

十、小结

Nginx 504 的核心是“超时”,先确认是超时而非崩溃,再按“慢查询 → 超时配置 → 反代链 → 后端阻塞 → 大响应”逐层排除。把超时做成分级、把长任务做成异步,504 就能从频发变偶发。

上一篇 zoxide 实战:用 Rust 智能 cd 让目录跳转快 10 倍
下一篇 Fastify 实战:用高性能 Node.js 框架构建 API 服务
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]