网站报错先别慌,看懂状态码的含义
网站突然打不开,浏览器里出现一行刺眼的错误码,很多新手站长的第一反应是重启服务器或者重装环境,结果折腾半天问题依旧。其实只要看懂状态码的含义,再顺着日志一层层往下查,大部分故障都能在十分钟内定位。HTTP 状态码是服务器给客户端的标准应答,5 开头的都是服务器端错误,其中个人站长最常遇到的是 500、502、503、504 这四个:500 Internal Server Error 表示服务器内部错误,通常是 PHP 代码抛了异常或者配置有问题;502 Bad Gateway 表示网关收到了无效响应,最常见的场景是 Nginx 连不上 PHP-FPM;503 Service Unavailable 表示服务暂时不可用,通常是服务器过载或者维护中;504 Gateway Timeout 表示网关超时,上游处理太久,Nginx 等不及了。
需要特别说明的是,同一个错误码在不同架构下含义可能不同。比如 502 在 Nginx 加 PHP-FPM 架构下是 PHP 进程的问题,在 Nginx 反代后端 API 的架构下可能是后端服务的问题。所以排查的第一步不是背定义,而是搞清楚自己网站的架构,然后去对应的日志里找线索。本文以最常见的 LNMP 架构为例,带你走一遍完整的排障流程。
排障第一站:永远先看错误日志
遇到任何报错,第一件事都是打开日志,而不是猜。LNMP 架构下有两类日志最值得看:Nginx 的错误日志和 PHP 的错误日志。Nginx 错误日志默认在 /usr/local/nginx/logs/error.log 或者 /var/log/nginx/error.log,查看最近几十行:
tail -n 50 /var/log/nginx/error.log
PHP 的错误日志位置取决于 php.ini 里的 error_log 配置,用下面的命令查:
php -i | grep error_log
tail -n 50 /usr/local/php/var/log/php-fpm.log
日志里每一行都带有时间戳和具体的错误信息,比如 connect() failed while connecting to upstream、Primary script unknown、Connection refused 等等。这些关键词直接指向问题根源,比状态码本身有价值得多。如果日志里什么都没有,说明错误日志级别配置太低,去 php.ini 里把 display_errors 和 error_reporting 调大,或者确认 Nginx 配置里的 error_log 指令确实生效了。很多站长排查半天没头绪,就是因为日志根本没打开。
502 Bad Gateway 的三大病因
502 是 LNMP 架构下最高频的错误,绝大多数情况都出在 Nginx 和 PHP-FPM 的通信上。第一个病因是 PHP-FPM 进程没启动。检查方法很简单:
ps aux | grep php-fpm
ss -tln | grep 9000
如果没有任何输出,说明 php-fpm 没跑起来,直接启动即可:
systemctl start php-fpm
第二个病因是 PHP-FPM 进程还在,但 Nginx 连不上它。LNMP 环境下 Nginx 通过两种方式和 PHP-FPM 通信:一种是 TCP 方式,默认监听 9000 端口;另一种是 Unix Socket 方式,比如 /tmp/php-cgi.sock。如果 Nginx 配置里写的是 socket 路径,而 php-fpm 实际监听的是 TCP,或者两者路径不一致,就会一直 502。用下面的命令看 php-fpm 到底监听在哪里:
ss -tlnp | grep php-fpm
ls -l /tmp/php-cgi.sock 2>/dev/null
然后去 Nginx 的站点配置里核对 fastcgi_pass 的地址是否一致。第三个病因是权限问题,这个坑最隐蔽:用 Unix Socket 通信时,php-fpm 进程以 www 用户运行,而 socket 文件的所有者或目录权限不对,Nginx 的 worker 进程就没有权限去连它。报错日志里通常会出现 Permission denied 字样。解决办法是统一 Nginx 和 php-fpm 的运行用户,并把 socket 文件的权限设置为 660,所属组保持一致。
504 Gateway Timeout 的排查思路
504 表示 Nginx 把请求转发给上游后,在规定时间内没等到响应,主动放弃并返回超时。最常见的场景是 PHP 脚本执行太慢,比如某个页面要跑好几秒甚至几十秒。Nginx 默认的 fastcgi_read_timeout 是 60 秒,如果 PHP 脚本执行时间超过这个值,就会 504。先看看是哪个 URL 在报错,再针对性地优化:
grep 504 /var/log/nginx/access.log | tail -20
如果只是个别接口超时,多半是慢查询或者第三方接口调用太慢,去 MySQL 慢查询日志里找找对应 SQL,加上索引或者拆分查询。如果是全站性 504,就要考虑是不是数据库连接数被占满,或者 php-fpm 的进程池已经全部被慢请求占满,新的请求排不上队。检查 php-fpm 的状态页和进程数:
ps aux | grep php-fpm | wc -l
grep pm.max_children /usr/local/php/etc/php-fpm.conf
pm.max_children 是 php-fpm 最多能启动的子进程数,默认值往往偏低。如果进程数长期顶着上限,说明这个值不够用,可以适当调大,但要注意调大意味着占用更多内存,一个 php-fpm 子进程通常占几十到上百 MB,要根据服务器内存量力而行,别把内存撑爆。
500 Internal Server Error 的定位方法
500 是所有 5 开头的错误里最需要看代码的。LNMP 环境下,500 绝大多数是 PHP 代码执行出错,比如语法错误、调用了不存在的函数、内存超限、文件权限不足等。PHP 默认把错误写到日志,不会直接显示给用户,所以第一步还是看 PHP 错误日志:
tail -n 100 /usr/local/php/var/log/php-fpm.log
日志里会有类似 PHP Fatal error: Uncaught Error: Call to undefined function 这样的记录,精确到文件和行号,照着改代码就行。如果日志是空的,可以去 php.ini 里临时开启 display_errors=On 并重启 php-fpm,让错误直接显示在页面上,定位完再关掉,千万别在生产环境长期开着。另外,文件权限导致的 500 也很常见:网站目录的所有者不是运行用户,PHP 无法写入缓存目录或者上传目录。用下面的命令检查:
ls -l /home/wwwroot/www.example.com
chown -R www:www /home/wwwroot/www.example.com
Typecho、WordPress 这类程序都会在运行时写缓存,目录权限不对就会白屏报 500。改完权限记得测试,别把所有目录都改成 777,那是安全大忌,正确做法是目录 755、文件 644、需要写入的目录单独设置权限。
503 Service Unavailable 的两个场景
503 在 LNMP 环境下有两种常见原因。第一种是服务器真的过载了:CPU 跑满、内存耗尽、负载飙升,php-fpm 或者 Nginx 处理不过来,拒绝新请求。用 top、free、uptime 三个命令快速看系统资源:
top
free -m
uptime
如果 load average 是 CPU 核数的好几倍,说明确实被压垮了,可能是被 CC 攻击或者有大量爬虫在抓,需要先限流封禁,再考虑升级配置。第二种 503 是 Nginx 配置里主动返回的,比如维护模式下所有请求统一返回 503 页面,这类情况看 Nginx 站点配置里有没有 return 503 或者 error_page 503 的指令就能确认。还有一种容易被忽略的情况:服务器磁盘满了,Nginx 写不了 access log,会静默返回 503,用 df -h 检查一下磁盘,往往一清空日志就好了。
一次完整的 502 实战排障案例
举一个真实的案例帮助你把流程串起来。某天早上站长收到监控告警,网站全部页面返回 502。站长没有急着重启,先看了 Nginx 错误日志,发现大量 connect() failed (111: Connection refused) while connecting to upstream,说明 Nginx 连 PHP-FPM 被拒绝。接着用 ss -tlnp 查看,发现 9000 端口没有监听,再 ps aux 确认 php-fpm 进程也不存在。查看 php-fpm 的日志,发现最后几行是内存不足导致进程被杀。用 free -m 确认服务器内存几乎耗尽,再用 du -sh 检查,发现是 MySQL 的 binlog 和慢查询日志把磁盘和内存资源都拖垮了。定位后,站长清理了过期日志、给 php-fpm 限制了 pm.max_children,并把 binlog 保留时间缩短,重启 php-fpm 后网站恢复正常。整个过程不到二十分钟,全程没有盲目重启,全靠日志逐层定位,这就是正确排障姿势的威力。
预防比排查更重要
排障能力再强,也不如让故障少发生。几个低成本高收益的预防措施值得现在就做:第一,给服务器配置监控脚本,进程挂掉、磁盘告急、网站 502 都第一时间推送告警,把被动发现变成主动预警;第二,定期检查 php-fpm 的进程数和内存占用,把 pm.max_children 调到一个和服务器内存匹配的合理值;第三,数据库连接数、慢查询、binlog 体积这些指标每月看一眼,别等磁盘写满才处理;第四,重要操作前先备份,修改配置前先 cp 一份原文件,出问题能秒级回滚。把这几条养成习惯,网站 502 和 500 会离你越来越远。日志是你排障最好的朋友,状态码是它递给你的线索卡,顺着线索走,再疑难的问题也能迎刃而解。