Nginx出现504 Gateway Time-out怎么办?超时原因排查与配置优化教程

Nginx出现504 Gateway Time-out怎么办?超时原因排查与配置优化教程

网站出现504 Gateway Time-out时,通常表示Nginx作为网关或反向代理,已经把请求交给PHP-FPM、Node.js、Java、Python、其他Web服务器或云端上游,但没有在规定时间内获得所需响应。

504与502并不完全相同。502更常见于上游地址错误、端口未监听、连接被拒绝、协议不匹配或上游返回无效响应;504则更偏向“上游存在,但响应等待超时”。不过,最终原因仍要以Nginx错误日志和完整代理链路为准。

常见诱因包括数据库慢查询、PHP-FPM进程不足、应用线程阻塞、第三方接口迟迟不返回、服务器CPU或内存压力过高、上游网络异常,以及Nginx、CDN或负载均衡器的超时设置不一致。

本文介绍一套从日志、上游服务、系统资源到超时配置的完整排查流程。示例适用于常见Linux服务器,具体服务名称、配置路径和模块需要根据实际环境调整。

一、先理解504发生在哪一层

典型请求链路可能是:

浏览器 → CDN/WAF → 负载均衡 → Nginx → PHP-FPM/应用服务 → 数据库或外部接口

链路中的每一层都可能设置连接和响应超时。页面显示Nginx样式的504,不一定代表问题根源就在Nginx;它只说明返回错误的这一层没有及时获得下一层响应。

常见场景包括:

  • Nginx等待PHP-FPM返回页面超时。
  • Nginx反向代理到Node.js或Java服务时读取响应超时。
  • 应用等待MySQL慢查询或锁超时。
  • 应用调用支付、短信、地图或其他第三方API时卡住。
  • CDN先于源站超时并返回自己的504页面。
  • 上游服务器负载过高,请求长时间排队。

排查的第一步不是修改配置,而是确认504由哪一层返回。

二、先检查Nginx错误日志

常见日志位置包括:

/var/log/nginx/error.log
/usr/local/nginx/logs/error.log

查看最近日志:

sudo tail -n 200 /var/log/nginx/error.log

实时观察:

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

如果Nginx由systemd管理,也可以查看:

sudo journalctl -u nginx --since "30 minutes ago"

重点寻找包含upstream timed out的记录,并注意后面的阶段说明。

while connecting to upstream

示例:

upstream timed out while connecting to upstream

表示Nginx连接上游时超时。常见原因包括:

  • 上游IP或端口不可达。
  • 防火墙或安全组丢弃连接。
  • 上游监听队列拥堵。
  • 网络路由或DNS异常。
  • 上游服务器已经过载。

while sending request to upstream

示例:

upstream timed out while sending request to upstream

表示Nginx向上游发送请求时超时。常见于:

  • 上传请求体较大。
  • 上游读取速度极慢。
  • 上游进程阻塞。
  • 网络连接质量较差。

while reading response header from upstream

示例:

upstream timed out while reading response header from upstream

这是最常见的504场景,表示上游迟迟没有返回响应头。通常需要检查:

  • PHP、Java、Node.js或Python程序是否执行过久。
  • 数据库查询是否缓慢或被锁阻塞。
  • PHP-FPM工作进程是否已满。
  • 应用是否等待外部接口。
  • 上游是否因CPU、内存或磁盘压力卡顿。

while reading upstream

如果上游已经开始响应,但后续一段时间没有继续传输数据,也可能触发读取超时。需要检查流式响应、文件生成、下载接口和上游网络。

三、确认是哪一个请求和上游超时

错误日志通常会包含:

  • 客户端IP。
  • 请求方法和URI。
  • 上游地址。
  • Host。
  • 请求时间。

例如:

upstream: "fastcgi://127.0.0.1:9000"

说明Nginx正在等待FastCGI服务,通常是PHP-FPM。

如果显示:

upstream: "http://127.0.0.1:8080"

则应继续检查监听8080端口的应用。

查看端口:

sudo ss -lntp

查看Unix Socket:

sudo ss -lxnp

确认应用进程:

ps -ef | grep -E 'php-fpm|node|java|gunicorn|uwsgi' | grep -v grep

不要只测试Nginx对外地址。应从Nginx所在服务器直接访问上游,以排除CDN和公网链路。

HTTP上游示例:

curl -v --connect-timeout 3 --max-time 30 http://127.0.0.1:8080/health

测试实际接口并记录时间:

curl -sS -o /dev/null \
  -w 'connect=%{time_connect} starttransfer=%{time_starttransfer} total=%{time_total}\n' \
  --max-time 60 \
  http://127.0.0.1:8080/example

如果直接访问上游同样很慢,问题主要在应用或其依赖,而不是Nginx转发本身。

四、在访问日志中加入上游耗时

仅靠错误日志很难判断超时是偶发还是持续发生。可以为Nginx访问日志加入请求总耗时和上游各阶段耗时。

http配置块中定义日志格式:

log_format upstream_timing
  '$remote_addr - $host "$request" status=$status '
  'request_time=$request_time '
  'upstream_addr=$upstream_addr '
  'upstream_status=$upstream_status '
  'upstream_connect_time=$upstream_connect_time '
  'upstream_header_time=$upstream_header_time '
  'upstream_response_time=$upstream_response_time';

在对应站点启用:

access_log /var/log/nginx/access_timing.log upstream_timing;

主要变量含义:

  • $request_time:Nginx处理整个请求的时间。
  • $upstream_connect_time:建立上游连接所用时间。
  • $upstream_header_time:收到上游响应头所用时间。
  • $upstream_response_time:接收上游完整响应所用时间。
  • $upstream_status:上游返回的状态码。
  • $upstream_addr:实际连接的上游地址。

修改后先检查配置:

sudo nginx -t

确认无误再平滑重载:

sudo systemctl reload nginx

不要直接重启Nginx来验证普通配置变更。配置语法错误可能导致服务无法重新启动。

五、PHP网站出现504如何排查

WordPress、WooCommerce、ThinkPHP、Laravel及其他PHP网站通常通过FastCGI连接PHP-FPM。

1. 检查PHP-FPM状态

服务名称可能因版本和发行版不同而变化:

sudo systemctl status php-fpm
sudo systemctl status php8.3-fpm

查看最近日志:

sudo journalctl -u php-fpm --since "30 minutes ago"

或:

sudo journalctl -u php8.3-fpm --since "30 minutes ago"

常见日志位置还包括:

/var/log/php-fpm/
/var/log/php8.3-fpm.log

2. 检查工作进程是否不足

PHP-FPM动态进程池常见参数包括:

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 10

如果日志出现类似“已达到pm.max_children”的信息,说明请求可能在FPM队列中等待。

不能只把pm.max_children不断调大。每个PHP工作进程都会占用内存,进程数过多可能导致Swap、OOM和整体性能下降。

可以先估算进程RSS:

ps --no-headers -o rss,cmd -C php-fpm |
  awk '{sum+=$1; n++} END {if(n) printf "workers=%d average_rss=%.1f MB total_rss=%.1f MB\n",n,sum/n/1024,sum/1024}'

不同发行版的进程名称可能包含版本号,可先使用ps确认后再统计。

3. 启用PHP-FPM慢日志

PHP-FPM支持在请求运行超过指定时间后记录PHP调用栈。池配置示例:

request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log

还可以设置:

request_terminate_timeout = 120s

request_slowlog_timeout用于诊断慢请求,request_terminate_timeout用于限制单个请求的最长执行时间,两者作用不同。终止时间设置过短可能中断合法的导入、备份和后台任务,设置前应理解业务执行时间。

修改PHP-FPM配置后,应先使用对应版本的配置测试命令。例如:

php-fpm -t

然后再重载实际服务。

4. 检查WordPress常见慢请求

常见来源包括:

  • 插件执行外部HTTP请求。
  • WooCommerce报表或批量任务。
  • 数据库表缺少索引。
  • wp-cron.php任务堆积。
  • 页面生成大量动态查询。
  • 图片处理或压缩。
  • 备份插件打包大目录。
  • 恶意爬虫反复访问搜索、登录或动态接口。

不要把备份、批量导出和长时间统计任务长期放在普通Web请求中执行。更稳妥的方式是使用后台队列或命令行任务。

六、Node.js、Java和Python应用如何排查

Node.js

检查:

  • 是否存在同步CPU密集任务阻塞事件循环。
  • 数据库连接池是否耗尽。
  • Promise是否一直等待外部接口。
  • 是否缺少HTTP客户端连接和读取超时。
  • 进程是否频繁进行垃圾回收。

查看服务日志:

sudo journalctl -u your-node-service --since "30 minutes ago"

如果由PM2管理:

pm2 status
pm2 logs --lines 200

Java

检查:

  • Web线程池是否已满。
  • 数据库连接池是否耗尽。
  • 垃圾回收暂停。
  • 锁竞争或死锁。
  • 下游HTTP调用超时。
  • 堆内存不足或频繁Full GC。

必要时结合线程转储、GC日志和应用监控分析,不应只通过Nginx层增加等待时间。

Python

Gunicorn、uWSGI等服务需要检查:

  • Worker数量与类型。
  • Worker超时和重启记录。
  • 同步Worker是否被慢请求长期占用。
  • 数据库与外部API等待。
  • CPU密集任务是否放在Web进程中。

长任务应迁移到Celery等任务队列,而不是让浏览器连接一直等待。

七、数据库慢查询和锁等待

上游应用迟迟不返回,数据库是最常见的下游瓶颈之一。

MySQL查看当前会话:

SHOW FULL PROCESSLIST;

查看InnoDB状态:

SHOW ENGINE INNODB STATUS\G

检查:

  • 长时间运行的SQL。
  • 等待锁的事务。
  • 大表全表扫描。
  • 缺少索引的连接和排序。
  • 连接池是否耗尽。
  • 报表、备份或批处理是否与在线请求争抢资源。

可以使用慢查询日志定位持续出现的慢SQL,但启用方式和阈值应根据环境配置。不要在未确认影响时开启记录全部查询。

如果应用每次请求都调用多个数据库查询,即使单条SQL不算特别慢,累计时间也可能超过网关超时。应结合应用性能追踪查看完整调用链。

八、检查服务器是否已经过载

查看CPU与负载:

uptime
top

查看内存和Swap:

free -h
vmstat 1 10

查看磁盘延迟:

iostat -xz 1 10

查看连接和监听队列:

ss -s
sudo ss -lntp

常见判断:

  • CPU长期接近满载:应用或数据库处理不过来。
  • Swap持续读写:内存压力可能拖慢所有请求。
  • 磁盘延迟很高:数据库、日志或临时文件操作被阻塞。
  • PHP-FPM或应用Worker全部繁忙:请求在上游内部排队。
  • 连接数异常增长:可能有流量突发、爬虫、攻击或连接泄漏。

如果问题只在固定时间发生,还应检查定时备份、日志压缩、数据统计、病毒扫描和面板任务。

九、Nginx超时参数应该怎么设置

反向代理HTTP上游

常见配置:

location /api/ {
    proxy_pass http://127.0.0.1:8080;

    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 60s;
}

作用大致为:

  • proxy_connect_timeout:建立上游连接的等待时间。
  • proxy_send_timeout:向上游连续两次写操作之间允许的等待时间。
  • proxy_read_timeout:从上游连续两次读操作之间允许的等待时间。

proxy_read_timeout不是简单限制整个响应必须在多少秒内完成。如果上游持续发送数据,长响应可能运行超过该时间;如果上游在设定时间内完全没有返回新数据,Nginx会关闭连接。

PHP-FPM上游

常见配置:

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php-fpm.sock;

    fastcgi_connect_timeout 5s;
    fastcgi_send_timeout 30s;
    fastcgi_read_timeout 60s;
}

fastcgi_read_timeout同样控制从FastCGI服务连续两次读取之间的等待时间,而不是简单的完整请求总时长。

不要全局设置过长超时

把所有超时统一设置成300秒或600秒,可能带来:

  • 慢请求长期占用PHP或应用Worker。
  • 连接和内存资源被更多请求占据。
  • 用户等待很久后仍然失败。
  • 故障发现时间变长。
  • 爬虫或恶意请求更容易消耗资源。

如果某个导出或上传接口确实需要更长时间,可以只在特定location中设置,并为应用、数据库和客户端建立一致的超时策略。

十、CDN和负载均衡器也可能先超时

如果网站前面还有CDN、WAF、云负载均衡或Ingress,需要检查每一层的超时限制。

例如:

客户端等待时间
↓
CDN源站响应超时
↓
负载均衡空闲超时
↓
Nginx proxy_read_timeout
↓
应用服务器请求超时
↓
数据库或第三方API超时

外层超时时间短于内层时,应用可能仍在执行,但客户端已经收到504。这样既浪费后端资源,也可能导致用户重复提交请求。

对于支付、下单、创建资源等写操作,应使用幂等设计和任务状态查询,避免用户刷新后产生重复操作。

十一、修改配置的正确流程

先查看Nginx最终加载的完整配置:

sudo nginx -T

查找超时参数:

sudo nginx -T 2>/dev/null |
  grep -E 'proxy_.*timeout|fastcgi_.*timeout|uwsgi_.*timeout|grpc_.*timeout'

修改后检查:

sudo nginx -t

确认成功再重载:

sudo systemctl reload nginx

继续观察:

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

配置可能分别存在于主配置、站点配置、面板生成文件和被include的片段中。只修改一个文件不代表最终配置一定生效,nginx -T比凭路径猜测更可靠。

十二、504故障的应急处理

网站已经大量返回504时,可以按以下顺序处理。

1. 保存现场

记录:

  • 发生时间和受影响URL。
  • Nginx错误日志。
  • 访问日志中的请求和上游耗时。
  • 应用、PHP-FPM和数据库日志。
  • CPU、内存、磁盘和连接状态。

不要立即重启全部服务,否则会丢失进程、连接和调用栈信息。

2. 限制异常流量

如果同一接口被高频访问,可以临时通过CDN、WAF、Nginx或应用层限流。应优先针对明确的接口和行为,而不是盲目封禁大量IP。

3. 暂停高消耗后台任务

确认备份、导入、报表或批处理正在挤占资源时,可以暂停任务来源并在低峰期恢复。

4. 恢复失效上游

如果某个应用实例已经无响应,可先从负载均衡中摘除。确认请求状态和数据风险后,再对单个实例进行平滑重启。

5. 谨慎提高超时

如果业务接口本来就需要较长执行时间,并且资源充足,可以临时提高对应location的读取超时。但应同时安排任务异步化或性能优化,不能把延长等待当作最终修复。

十三、容易踩的错误

只修改proxy_read_timeout

PHP网站通常使用fastcgi_pass,此时只修改proxy_read_timeout不会影响FastCGI读取超时。应先通过nginx -T确认实际使用的上游模块。

同时把所有超时改得很大

这会掩盖慢SQL、线程池耗尽和外部接口无超时等根因,并增加资源被长期占用的风险。

看到504就重启Nginx

Nginx多数时候仍在正常接收请求,真正卡住的是PHP-FPM、应用、数据库或外部服务。重启Nginx通常只能中断现有连接。

只看CPU使用率

CPU不高并不代表服务正常。锁等待、连接池耗尽、网络等待、磁盘I/O和Swap都可能让上游无法及时响应。

忽略重复提交

用户收到504时,后端操作可能仍然成功完成。订单、支付、发送邮件和创建云资源等操作必须具备幂等控制。

十四、推荐的完整排查顺序

遇到504时,可以按照以下顺序操作:

  1. 确认错误由CDN、负载均衡还是Nginx返回。
  2. 查看Nginx错误日志,识别连接、发送或读取超时。
  3. 从日志中找出具体URL和上游地址。
  4. 从Nginx服务器直接测试上游接口。
  5. 在访问日志加入请求和上游各阶段耗时。
  6. 检查PHP-FPM或应用Worker、线程池和连接池。
  7. 检查数据库慢查询、锁等待和外部API调用。
  8. 检查CPU、内存、Swap、磁盘延迟和连接数。
  9. 使用nginx -T确认真正生效的超时配置。
  10. 只针对有明确需求的接口调整超时。
  11. 将导出、备份和批处理改为异步任务。
  12. 建立调用链监控、慢请求日志和容量告警。

总结

Nginx出现504 Gateway Time-out,本质上是网关没有在预期时间内获得上游响应。真正原因可能位于PHP-FPM、应用程序、数据库、第三方API、服务器资源或更外层的CDN和负载均衡器。

正确处理方式是先读取Nginx错误日志,判断超时发生在连接、发送还是读取阶段,再通过上游耗时日志、应用慢日志和系统监控定位瓶颈。

适当调整proxy_read_timeoutfastcgi_read_timeout可以满足合理的长请求,但不能替代SQL优化、连接池治理、Worker容量规划和任务异步化。盲目延长所有超时,只会让失败来得更晚,并让慢请求占用更多资源。

知识库

Linux磁盘还有空间却提示No space left on device?inode占满排查与清理教程

2026-8-10 16:44:48

实操指南知识库

存储优化策略

2024-12-24 14:54:21