502 和 504 是 VPS 建站里最常见、也最容易让人慌的两个错误。
浏览器打开网站,只看到:
502 Bad Gateway
504 Gateway Timeout
很多人第一反应是域名坏了、证书坏了、服务器被墙了。其实大多数时候不是这些。502/504 通常说明:Nginx 还活着,但它后面的应用没正常回应。
可以把它理解成:门卫还在门口,但后厨没人接单,或者后厨接单太慢。
这篇按实战顺序讲:先从 Nginx 日志确认原因,再查 PHP-FPM、Gunicorn、Node、Docker 上游,最后看超时、进程池、内存和磁盘。不要上来就重装环境,也不要只会重启整台 VPS。
简单说:
- 502 Bad Gateway:Nginx 连后端失败,或者后端返回了无效响应
- 504 Gateway Timeout:Nginx 连上了,但等后端响应等太久
常见场景:
| 错误 | 常见原因 |
|---|---|
| 502 | PHP-FPM 没启动、socket 路径错、端口没监听、后端进程崩了 |
| 502 | Unix socket 权限不对、Nginx 访问不了 PHP-FPM socket |
| 502 | Docker 容器挂了、反代端口写错 |
| 504 | PHP/应用执行太慢、数据库卡住、接口阻塞 |
| 504 | Nginx proxy_read_timeout / fastcgi_read_timeout 太短 |
| 504 | 后端进程池满了,请求排队太久 |
所以排查方向不是“网站打不开”,而是“前端代理到后端这段链路哪里断了”。
不要猜。先看日志:
sudo tail -n 100 /var/log/nginx/error.log
实时看:
sudo tail -f /var/log/nginx/error.log
然后刷新网页,看新错误是什么。
常见几类:
connect() failed (111: Connection refused) while connecting to upstream
说明 Nginx 去连后端端口或 socket,被拒绝。后端可能没启动,或者端口写错。
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied)
说明 socket 权限问题。
upstream timed out (110: Connection timed out) while reading response header from upstream
说明后端响应太慢,偏 504/超时方向。
no live upstreams while connecting to upstream
说明 upstream 组里没有可用后端。
看懂这一行,基本就知道下一步该查哪里。
如果你最近改过站点配置,先检查 Nginx:
sudo nginx -t
如果配置没问题,再 reload:
sudo systemctl reload nginx
不要直接 restart。nginx -t 能提前发现配置语法错误,reload 也比 restart 更平滑。
再查状态:
systemctl status nginx --no-pager
journalctl -u nginx --since "30 minutes ago" --no-pager
Nginx 官方文档里也明确区分了 error_log 的级别和调试日志。普通排障先看 error log 就够;debug log 适合复杂问题,不要一开始就打开,否则日志会刷得很快。
WordPress、Typecho、Laravel、ThinkPHP 这类 PHP 网站,502 大概率和 PHP-FPM 有关。
先看服务名。不同系统可能叫:
systemctl list-units | grep fpm
常见是:
php8.1-fpm
php8.2-fpm
php8.3-fpm
查状态:
systemctl status php8.2-fpm --no-pager
journalctl -u php8.2-fpm --since "30 minutes ago" --no-pager
如果没启动:
sudo systemctl restart php8.2-fpm
但重启后不要就结束。继续看日志,确认是不是配置错误、内存不足、扩展加载失败。
Nginx 和 PHP-FPM 通常通过 Unix socket 或 TCP 端口通信。
先找 Nginx 里写了什么:
sudo grep -R "fastcgi_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
你可能看到:
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
那就确认 socket 是否存在:
ls -l /run/php/
如果 Nginx 配的是 php8.1-fpm.sock,但系统实际只有 php8.2-fpm.sock,那就是典型 502。
如果 Nginx 配的是 TCP:
fastcgi_pass 127.0.0.1:9000;
就看端口:
ss -lntp | grep 9000
Nginx 官方 FastCGI 文档里,fastcgi_pass 就是决定请求交给哪个 FastCGI 后端的关键指令。路径或端口错了,Nginx 再正常也没用。
如果日志里有:
Permission denied while connecting to upstream
先看 socket 权限:
ls -l /run/php/php8.2-fpm.sock
再看 Nginx 运行用户:
ps -o user,group,cmd -C nginx
PHP-FPM pool 配置里通常有这些项:
listen = /run/php/php8.2-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
常见配置文件位置:
/etc/php/8.2/fpm/pool.d/www.conf
改完先检查再重启 PHP-FPM:
sudo php-fpm8.2 -t
sudo systemctl restart php8.2-fpm
sudo systemctl reload nginx
不同发行版命令可能略有差异。如果 php-fpm8.2 -t 不存在,可以用:
php-fpm8.2 -tt
或者看当前系统的 PHP-FPM binary 名称。
如果网站流量上来,或者某个 PHP 请求卡住,PHP-FPM 进程池可能耗尽。
日志里可能看到:
server reached pm.max_children setting
先查 PHP-FPM 日志:
journalctl -u php8.2-fpm --since "1 hour ago" --no-pager
再看 pool 配置:
sudo grep -E "^pm\.|^request_" /etc/php/8.2/fpm/pool.d/www.conf
常见项:
pm = dynamic
pm.max_children = 10
pm.start_servers = 2
pm.min_spare_servers = 2
pm.max_spare_servers = 5
request_terminate_timeout = 60s
PHP 官方 PHP-FPM 配置文档里有 pm.max_children、pm.max_requests、request_terminate_timeout、slowlog、request_slowlog_timeout 等参数。不要只会把 pm.max_children 无限调大,它会吃内存。
粗略估算可以这样看 PHP 进程内存:
ps -ylC php-fpm8.2 --sort:rss
如果每个 PHP 子进程 80MB,机器只有 1GB 内存,pm.max_children 设置 50 就很危险。它可能没解决 502,反而把 VPS 打到 OOM。
PHP-FPM 官方支持 status page,可以看到进程池状态,比如 active processes、listen queue、max children reached、slow requests。
pool 配置里加:
pm.status_path = /fpm-status
ping.path = /fpm-ping
Nginx 里只允许本机访问:
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
reload 后测试:
curl http://127.0.0.1/fpm-status
curl http://127.0.0.1/fpm-status?json
curl http://127.0.0.1/fpm-ping
如果 listen queue 经常不为 0,或者 max children reached 增长,说明 PHP-FPM 处理不过来了。
如果你用的是 Django、Flask、FastAPI,后端可能是 Gunicorn 或 Uvicorn。
先看 Nginx upstream 配置:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
如果是:
proxy_pass http://127.0.0.1:8000;
就看端口:
ss -lntp | grep 8000
看服务:
systemctl status gunicorn --no-pager
journalctl -u gunicorn --since "30 minutes ago" --no-pager
如果服务名不确定:
systemctl list-units | grep -Ei "gunicorn|uvicorn|app"
ps -ef | grep -Ei "gunicorn|uvicorn|python"
如果 Nginx 能连到端口,但 504,通常要看应用日志、数据库连接、慢查询、第三方 API 超时。
Node 应用常见是 Nginx 反代到本地端口。
看 Nginx:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
看端口:
ss -lntp | grep -E ':3000|:8080|:8000'
如果用 PM2:
pm2 status
pm2 logs --lines 100
常见 502 原因:
- Node 进程崩了
- 端口改了,Nginx 没改
- 环境变量没加载
- 应用启动慢或卡死
- 内存爆了被系统杀掉
看 OOM:
journalctl -k --since "2 hours ago" | grep -Ei "out of memory|killed process"
Docker 里跑网站,Nginx 可能在宿主机,也可能在容器里。
先看容器状态:
docker ps
docker compose ps
看日志:
docker logs 容器名 --tail 100
docker compose logs --tail 100 服务名
看端口映射:
docker ps --format 'table {{.Names}}\t{{.Ports}}'
如果 Nginx 写的是:
proxy_pass http://127.0.0.1:3000;
但 Docker 根本没把 3000 映射到宿主机,或者容器内服务监听在 127.0.0.1 而不是 0.0.0.0,就会 502。
容器内测试:
docker exec -it 容器名 sh
curl -I http://127.0.0.1:3000
宿主机测试:
curl -I http://127.0.0.1:3000
这两个结果能帮你判断问题在容器内,还是容器到宿主机的端口映射。
如果错误是 504,后端通常不是完全挂了,而是太慢。
Nginx 里常见参数:
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
Nginx 官方文档里有 fastcgi_read_timeout、fastcgi_connect_timeout 这些参数。但不要一看到 504 就把 timeout 改成 600 秒。
如果一个页面要跑 5 分钟,真正问题可能是:
- 数据库慢查询
- 外部 API 卡住
- 文件上传太大
- PHP 任务不该在 Web 请求里执行
- 队列没拆出来
- CPU / IO 被打满
先看资源:
top
free -m
df -h
df -i
iostat -xz 1
如果没有 iostat:
sudo apt install sysstat
再看应用日志和数据库慢查询。调 timeout 只能止痛,不能治病。
网站报 502/504,我一般按这个顺序:
curl -I https://example.com
sudo tail -n 100 /var/log/nginx/error.log
sudo nginx -t
systemctl status nginx --no-pager
systemctl list-units | grep -Ei "php|fpm|gunicorn|uvicorn|docker"
ss -lntp
如果是 PHP:
systemctl status php8.2-fpm --no-pager
journalctl -u php8.2-fpm --since "30 minutes ago" --no-pager
sudo grep -R "fastcgi_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
ls -l /run/php/
如果是反代应用:
sudo grep -R "proxy_pass" /etc/nginx/sites-enabled /etc/nginx/conf.d
ss -lntp | grep -E ':3000|:8000|:8080'
journalctl -u gunicorn --since "30 minutes ago" --no-pager
pm2 status
如果是 Docker:
docker ps
docker compose ps
docker compose logs --tail 100
先定位链路断点,再决定是重启服务、改 socket、调进程池,还是查慢请求。
重启 Nginx、PHP-FPM、应用服务有时能恢复,但它可能只是把现场清掉。
更好的顺序是:
- 截取 Nginx error.log
- 记录后端服务状态
- 看端口/socket 是否存在
- 看是否 OOM、磁盘满、inode 满
- 再决定 reload/restart
如果你只是盲目执行:
sudo reboot
机器起来后网站好了,你也不知道根因是什么。下次高峰期还会再来一次。
- 已确认是 502 还是 504
- 已查看
/var/log/nginx/error.log - 已执行
nginx -t - 已确认 Nginx 服务正常
- PHP 站点已检查 PHP-FPM 状态和日志
- 已核对
fastcgi_pass的 socket/端口是否存在 - 已检查 socket 权限和 Nginx/PHP-FPM 用户
- 已检查
pm.max_children是否打满 - Python/Node 应用已确认进程和监听端口
- Docker 应用已确认容器状态、日志和端口映射
- 504 已检查慢请求、数据库、外部 API、CPU/IO
- 没有只靠重启掩盖根因
502/504 最关键的思路是:Nginx 通了,不代表网站就通了。你要查的是 Nginx 到后端这段链路:连不上、没权限、后端挂了、队列满了,还是响应太慢。把这几层拆开查,问题通常很快就能落到具体服务上。
