
网站出现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时,可以按照以下顺序操作:
- 确认错误由CDN、负载均衡还是Nginx返回。
- 查看Nginx错误日志,识别连接、发送或读取超时。
- 从日志中找出具体URL和上游地址。
- 从Nginx服务器直接测试上游接口。
- 在访问日志加入请求和上游各阶段耗时。
- 检查PHP-FPM或应用Worker、线程池和连接池。
- 检查数据库慢查询、锁等待和外部API调用。
- 检查CPU、内存、Swap、磁盘延迟和连接数。
- 使用
nginx -T确认真正生效的超时配置。 - 只针对有明确需求的接口调整超时。
- 将导出、备份和批处理改为异步任务。
- 建立调用链监控、慢请求日志和容量告警。
总结
Nginx出现504 Gateway Time-out,本质上是网关没有在预期时间内获得上游响应。真正原因可能位于PHP-FPM、应用程序、数据库、第三方API、服务器资源或更外层的CDN和负载均衡器。
正确处理方式是先读取Nginx错误日志,判断超时发生在连接、发送还是读取阶段,再通过上游耗时日志、应用慢日志和系统监控定位瓶颈。
适当调整proxy_read_timeout或fastcgi_read_timeout可以满足合理的长请求,但不能替代SQL优化、连接池治理、Worker容量规划和任务异步化。盲目延长所有超时,只会让失败来得更晚,并让慢请求占用更多资源。




