error_log不记录“权限溢出”,实际需开启debug级别才能捕获open()/stat()失败的(13: permission denied)等真实错误码,据此区分文件系统权限、selinux拦截或路径计算错误。

Nginx 的 error_log 本身不记录“权限溢出”——这个说法存在概念混淆。实际中并不存在“权限溢出”这一错误类型,你真正遇到的,通常是 403 Forbidden(权限不足)或 404 Not Found(路径/文件不可达),而它们在 error_log 中的表现,取决于错误根源和日志级别配置。
要让 error_log 准确、有用地反映静态资源访问失败的真实原因,关键不是“记录权限溢出”,而是确保它能暴露底层系统拒绝访问的具体路径与错误码。以下是实操要点:
error_log 必须开启 debug 级别才能看清权限类失败细节
默认 warn 或 error 级别往往只写 “client denied” 或空行,根本看不出是哪个目录缺 x、哪个文件缺 r,更无法区分是普通权限问题还是 SELinux 拦截。
必须做两件事:
- 在
nginx.conf的http或对应server块中添加:error_log /var/log/nginx/error.log debug; - 确保 Nginx 编译时包含
--with-debug参数(否则 debug 不生效)
改完后执行nginx -t && nginx -s reload,再触发一次静态请求,才能看到形如:open() "/var/www/static/app.js" failed (13: Permission denied)
或stat() "/var/www/static/app.js" failed (13: Permission denied)
从 error_log 报错反推三类真实权限问题
看到 (13: Permission denied) 后,不能直接改 chmod,要先看调用的是 open() 还是 stat():
-
open() ... failed (13)→ 文件系统权限问题- 检查
nginx worker进程用户(如www-data)对目标文件是否有r权限 - 更关键的是:对每一级父目录(如
/var、/var/www、/var/www/static)是否有x权限(缺x= 无法进入目录,比缺r更隐蔽)
- 检查
-
stat() ... failed (13)→ SELinux 或 AppArmor 主动拦截- 在 CentOS/RHEL 上极常见,
ls -Z /var/www/static/app.js查上下文 - 若上下文不对(如
httpd_sys_content_t缺失),需用semanage fcontext -a -t httpd_sys_content_t "/var/www/static(/.*)?"修复
- 在 CentOS/RHEL 上极常见,
- 同时报
(2: No such file or directory)→ 路径计算错误,不是权限问题- 用
return 200 "$request_filename";验证 Nginx 实际拼出的路径是否正确 - 常见于
alias少斜杠、location正则捕获异常、大小写不一致
- 用
log_not_found 可静默 404 类日志,但不掩盖权限问题
log_not_found off; 仅屏蔽 open() ... (2: No such file or directory) 这类“文件不存在”的 warn 日志,对 (13: Permission denied) 完全无效——它属于 error 级别,始终会记。
所以:
- SPA 应用中大量 404(最终 fallback 到 index.html)可关
log_not_found避免刷屏 - 但只要出现
Permission denied,无论log_not_found开关与否,该报错都会出现在error_log中,且必须处理
不复杂但容易忽略











