排查500错误应先定位故障层:用curl -v看server、content-type和connection头;再确认日志真实生效(检查php log_errors、nginx error_log级别及selinux限制);直连php-fpm验证解析问题;临时开启display_errors需ip白名单;最后排查磁盘、inode、时间同步等“不可见变更”。

看日志前先确认日志是否真在记录
生产环境的 error_log 经常是“看起来有,其实没写”——比如 PHP 的 log_errors 被设为 Off,或 Nginx 的 error_log 级别设成了 warn,导致致命错误被过滤掉。
实操建议:
- 检查 PHP 是否真在记错:
php -i | grep "log_errors\|error_log",确认log_errors = On且error_log指向可写路径(如/var/log/php_errors.log) - 验证 Nginx 日志是否生效:
tail -f /var/log/nginx/error.log,另开终端发个明显错请求(如访问一个不存在的 PHP 文件),看有没有新行输出 - 警惕 SELinux 或 AppArmor 干预:
ausearch -m avc -ts recent | grep nginx,若出现avc: denied,说明内核强制模块拦了写日志行为
用 curl -v 快速判断是哪一层崩了
500 错误可能来自 Web 服务器(Nginx/Apache)、PHP-FPM、数据库连接、甚至上游代理。盲目翻日志前,先定位故障层更省时间。
实操建议:
- 执行
curl -v https://yoursite.com/api/test.php,重点看三处:
— 响应头中的Server:字段(是 nginx 还是 Apache?)
—Content-Type:是text/html(Web 服务器返回的通用 500 页面)还是application/json(应用层自己吐的错误)
—Connection:是否被提前关闭(暗示进程崩溃) - 绕过 Web 服务器直连 PHP-FPM:
SCRIPT_FILENAME=/var/www/html/test.php SCRIPT_NAME=/test.php REQUEST_METHOD=GET cgi-fcgi -bind -connect /run/php/php8.2-fpm.sock,若报错立刻暴露是 PHP 解析或扩展问题 - 若响应头带
X-Powered-By: PHP/8.2.12却无正文,大概率是 PHP 启动即 fatal(如扩展加载失败),此时查php -m和dmesg | tail
临时开启错误显示必须加白名单限制
线上绝不能全局开 display_errors = On,但调试关键接口时又需要看到堆栈。折中方案是仅对运维 IP 生效,既安全又高效。
实操建议:
- 在 Nginx 配置里加条件判断:
if ($remote_addr = "203.0.113.42") {<br> fastcgi_param PHP_VALUE "display_errors=On \n error_reporting=E_ALL";<br>} - PHP-FPM 池配置中启用
php_admin_value[error_log],确保错误仍落盘,不依赖 stdout - 切忌在代码里写
ini_set('display_errors', '1')—— 若该文件本身因语法错误无法解析,这行根本不会执行 - 验证是否生效:访问时加
?debug=1,并在 PHP 中用if ($_GET['debug'] === '1' && $_SERVER['REMOTE_ADDR'] === '203.0.113.42') { ini_set(...); },双重保险
查完日志后,优先排除「不可见变更」
很多 500 是“什么都没改却崩了”,真实原因是磁盘满、inode 耗尽、系统时间跳变、或 crontab 自动清理了 session 目录——这些不会出现在 Git 记录里,但会直接触发权限或路径错误。
实操建议:
- 立刻运行:
df -h /(看根分区)、df -i /var(看 inode)、ls -ld /var/lib/php/sessions(确认目录存在且 PHP-FPM 用户可写) - 检查系统时间是否异常:
timedatectl status,若System clock synchronized: no,NTP 失步可能导致 JWT 或 SSL 证书校验失败,间接引发 500 - 看最近有没有自动更新:
grep "start.*package" /var/log/apt/history.log | tail -5(Debian/Ubuntu),某些 PHP 扩展升级后 ABI 不兼容,会导致Segmentation fault写入日志但无堆栈 - 用
strace -p $(pgrep php-fpm | head -1) -e trace=openat,connect抓一个卡住的 worker,能直接看到它试图打开哪个文件或连哪个 socket 失败
最常被忽略的是日志轮转脚本删错了目录,或者监控 agent 把 /tmp 清空导致 session 无处落盘——这类问题不看系统层状态,光盯代码永远找不到。










