网站502或504错误:Nginx与PHP-FPM排查

502表示网关没有得到有效的上游响应,504表示等待上游响应超时。错误可能出在前置代理、Nginx与应用之间,或应用依赖的数据库。本篇适用于Linux上的Nginx反向代理或PHP-FPM网站,按下面顺序定位。

一、先确认是哪一层返回错误

记下完整网址、请求方法、报错时间和时区。用同一条请求对照前置代理与源站日志;只看到错误页面上的“Nginx”,还不能确定出错的是哪一台服务器。复测选择普通查看页面,付款、下单等操作保留原请求记录,避免重复提交。

使用CDN或前置代理时,若源站允许当前测试来源直接访问,可在终端做一次对照。下面是示例,域名、源站IP和路径均需替换成自己的:

curl --noproxy '*' --resolve example.com:443:203.0.113.10 \
  --connect-timeout 5 --max-time 90 -sS -o /dev/null \
  -w 'HTTP=%{http_code} IP=%{remote_ip}\n' \
  'https://example.com/problem-page'

此命令把连接指向指定IP,仍按域名发送请求并校验证书。直连成功而公网入口失败,继续核查前置代理的回源配置与日志;直连也报502/504,继续查源站。若显示证书错误或HTTP=000,先处理对应连接问题,不能当成源站返回了504。需要登录的页面应对照原请求,不能拿未登录首页的结果替代。

二、找到本站配置与错误日志

先通过SSH登录源站,查看正在运行的Nginx入口:

ps -eo pid,args | grep '[n]ginx: master'

下面以系统安装的nginx为例。若上一步显示其他可执行文件路径,或启动参数带有-c、-p,检查时使用同一个入口和参数;容器安装在相应容器内检查。

sudo nginx -t
sudo nginx -T

-t检查语法及引用文件能否打开;出现syntax is ok和test is successful才算通过。-T还会打印当前磁盘配置。找到本站server_name下匹配故障路径的location,记下proxy_pass或fastcgi_pass的目标,以及error_log位置。

配置检查通过不代表上游正常,也不证明这份文件已被运行中的进程加载。完整配置可能含内部地址或凭据,只在自己的终端查看,提交问题时截取相关几行并脱敏。

将下面路径换成本站实际错误日志,查找报错时间、请求路径和upstream字段一致的记录:

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

末尾100行没有相关记录时,按时间继续查询,方法见用Nginx日志定位网站访问问题。同时确认日志路径、时区及是否关闭访问日志,不能仅凭“没查到”认定请求未到源站。

三、核对上游是否监听在正确位置

sudo ss -lntp
sudo ss -lxnp

第一条查看监听中的TCP端口,第二条查看Unix socket。把结果与日志中的上游地址逐项对照。例如Nginx连接127.0.0.1:9000,而FPM实际监听的是一个socket文件,两边就没有连到同一位置。监听存在后仍要看服务能否处理请求。PHP-FPM使用FastCGI协议,不能把9000端口当HTTP服务用curl检查,也无需为此开放到公网。

PHP服务由systemd管理时,先查实际服务名:

systemctl list-unit-files --type=service --no-pager | grep -i fpm

下面仅以查到php8.3-fpm.service为例,执行前换成自己的服务名:

sudo systemctl status php8.3-fpm.service --no-pager -l
sudo journalctl -u php8.3-fpm.service --since '15 minutes ago' --no-pager -n 100

failed或inactive时先读启动错误;active时继续核对监听和请求日志。故障发生更早时调整查询时间。服务不在列表中,或journal没有记录,可从安装面板、容器或FPM配置的error_log查找;宝塔多PHP环境先确认网站实际使用的版本,见PHP版本与扩展检查。

四、根据错误内容采取对应处理

日志关键词或现象 优先核查 处理方向
Connection refused 对应IP和端口是否有监听,目标是否填错 先修复服务启动失败,或改正已核实不一致的上游地址;安全规则明确拒绝也可能出现此结果。
连接socket时出现No such file or directory 实际FPM监听路径、服务状态、容器内外路径 使Nginx指向实际存在的监听端点;不要手工创建一个同名普通文件代替socket。
连接socket时出现Permission denied Nginx工作进程用户、父目录访问权限、socket归属与ACL 核对FPM池的listen.owner、listen.group、listen.mode或ACL;若启用了SELinux/AppArmor,同时查对应拒绝日志。
upstream timed out 连接阶段超时,还是等待响应阶段超时 前者查目标、路由与访问规则;后者查应用日志、FPM繁忙/慢请求、数据库及外部接口耗时。

权限问题可用下面命令查看Nginx工作进程用户,以及socket路径各级目录的权限;第二条路径要换成实际socket:

ps -eo user,pid,args | grep '[n]ginx: worker'
sudo namei -l /run/php/php8.3-fpm.sock

只修正缺少的目录访问或socket连接权限,不要用chmod 777或关闭安全模块代替定位。

proxy_read_timeout和fastcgi_read_timeout限制的是两次读取之间的等待时间。应用卡住、进程不足或数据库慢时,单纯增大这个数值会让请求等得更久,应先找到耗时发生在哪一段。

五、修复后怎么确认

修改前保留原配置;改正已确认的问题后,先通过对应服务的配置检查,再按该服务的正常方式重载。复测原来的路径和操作,核对预期内容、状态码及同一时间段日志;还应检查原本正常的页面。只打开首页成功,不能代表原故障已恢复。

需要协助时,向售后工单提供报错时间、脱敏后的请求地址、最近改动、直连对照结果及相关错误日志。不要上传完整配置或Cookie,具体方法见截图与日志脱敏。

技术依据:Nginx命令行与代理模块文档、PHP-FPM配置手册、systemd及curl官方文档、HTTP语义规范。