最常见原因是error_log路径不可写或属主不匹配;php进程需对日志目录有写权限,父目录不能root-only可写;容器中需挂载时加:z/:z;display_errors=on会暴露敏感信息,应禁用;error_log()不宜用于业务日志,推荐psr-3库;日志须过滤敏感字段,避免合规风险。

php.ini里log_errors=On但日志文件没写入
最常见原因是error_log路径不可写,或属主不匹配。PHP进程(如www-data)必须对目标目录有写权限,且父目录不能是root-only可写。
实操建议:
- 运行
ps aux | grep php-fpm确认worker用户,再用ls -ld /var/log/php/检查目录权限 - 别直接设
error_log = /var/log/php/error.log——先确保/var/log/php/存在且chown www-data:www-data /var/log/php - 若用容器,挂载卷时加
:z或:Z(SELinux环境),否则error_log会静默失败 - 测试:在脚本里写
trigger_error('test', E_USER_WARNING),然后tail -f /var/log/php/error.log看是否实时出现
开发环境开了display_errors,上线后忘了关
display_errors = On在生产环境不是“少点日志”,而是直接把堆栈、绝对路径、数据库表名甚至部分配置暴露给攻击者。
实操建议:
- 别只改CLI的
php.ini——FPM用的是独立配置,查php --ini和php-fpm -t确认生效文件 - 在
www.conf里加php_admin_flag[display_errors] = off,比改全局php.ini更可靠 -
ini_set('display_errors', '0')在代码里无效,因为该指令是PHP_INI_SYSTEM级别,运行时无法覆盖 - 上线前执行
curl -I https://yoursite.com | grep X-Powered-By,如果返回含PHP/8.2.12之类版本号,说明expose_php = On也开着,一并关掉
用error_log()写自定义日志却混进PHP错误日志
error_log()默认输出到error_log指定位置,和PHP Fatal error日志混在一起,导致审计时分不清是框架报错还是你主动记录的业务事件。
实操建议:
- 业务日志坚决不用
error_log()——它本质是错误报告通道,不是通用日志接口 - 要写结构化业务日志,用PSR-3兼容库(如Monolog),输出到独立文件:
storage/logs/app.log - 如果非要用
error_log(),至少指定路径:error_log("msg", 3, "/var/log/myapp/access.log"),但注意该路径也要提前赋权 - 避免
error_log(json_encode($data))这种写法——JSON换行符会被截断,日志解析器会丢数据
日志格式没过滤敏感字段,审计时踩合规红线
一条error_log("login fail for {$user->email} pwd={$user->password}")就能让PCI DSS或GDPR审计直接fail。
实操建议:
- 所有日志内容过白名单:只允许
user_id、ip、event、timestamp等非敏感字段入库 - 对
$_POST、$_GET、$request做预处理,删掉password、token、card_number等键 - 用
preg_replace('/"password"\s*:\s*"[^"]+"/i', '"password":"[REDACTED]"', $json)做兜底清洗 - 审计时重点查
laravel.log、yii2/runtime/logs/app.log——这些框架日志常含SQL语句或完整请求体,比PHP原生日志更危险
error_log()或调logger->info()前,多问一句:这行会不会出现在明天的安全扫描报告里?php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











