Nginx 日志出现 Permission denied?目录权限、运行用户与 SELinux 排查

网站文件存在却被 Nginx 拒绝读取,问题可能出在上层目录搜索权限、worker 身份、ACL 或 SELinux 标签。本文从错误日志中的实际对象入手,检查静态文件与 PHP-FPM socket,说明持久化标签的配置边界、最小权限修复和回滚记录,并提供原请求验收方法,避免用递归 777 或关闭安全策略掩盖问题。
Nginx 日志出现 Permission denied?目录权限、运行用户与 SELinux 排查

网站迁移到新目录后,首页突然返回 403;文件明明存在,Nginx 错误日志却出现 Permission denied。把文件改成可读仍然没用,甚至用 root 查看一切正常。这里容易漏掉两个条件:访问文件需要经过上层目录,实际处理请求的进程也未必是你登录服务器时使用的用户。

遇到这个报错,应先从错误日志里的失败对象判断检查方向。静态文件、PHP-FPM 的 Unix socket 和代理网络连接,对应的权限边界不同。本文以 Linux 上的 Nginx 为主,说明如何定位静态目录访问,并单独列出 socket 和 SELinux 的检查入口。命令均为示例,没有在读者的服务器上执行过。

日志里到底是哪一步被拒绝

Permission denied 通常对应系统返回的 EACCES,但它本身不会告诉你应该修改哪条规则。需要连同调用名称、路径和请求一起看。

  • open() ... failed (13: Permission denied):重点检查被打开的文件、目录路径、访问身份和安全策略。
  • connect() to unix:... failed (13: Permission denied):检查 Unix socket、父目录及监听端的权限设置。
  • connect() ... failed (13: Permission denied) while connecting to upstream:还可能涉及 SELinux 对网络连接的限制或其他本机安全规则,不能靠修改网站文件权限解决。

浏览器返回 403 不一定由权限引起。directory index ... is forbidden 可能只是目录没有可用首页文件且未开启目录列表;access forbidden by rule 则应检查访问控制规则。两者都不适合直接套用 chmod。

从配置中的实际 error_log 位置截取相关记录,按请求时间和域名对应。不要把完整日志直接公开,URL 查询参数、内网地址和文件路径可能包含敏感信息。

确认运行用户和真正命中的路径

普通部署中,master 进程可能以 root 启动,worker 使用权限较低的账户处理请求。看到 master 是 root,不能据此判断静态文件应该读得开。

ps -eo user,pid,ppid,args | grep '[n]ginx'

同时检查当前实例实际加载的配置。Nginx 的 user 指令控制 worker 的用户和组,但非 root 启动、容器镜像或多实例环境可能有所不同。进程列表与运行方式都需要核对。

在获得本机配置读取权限后,可用下面的命令检查配置:

sudo nginx -t
sudo nginx -T

-T 会输出完整配置,可能包含内部域名、认证信息或其他敏感设置,请在本机查看,不要直接粘贴到公开论坛。多实例或定制安装应使用服务实际采用的二进制、配置路径和参数,避免检查了另一个 Nginx。

检查对应 server、location 下的 root、alias、try_files。它们决定请求最后访问哪个对象。尤其是 alias 和符号链接,不能仅凭浏览器 URL 猜磁盘路径。路径配错时,给另一个目录增加权限不会产生效果。

文件可读,上层目录仍可能挡住访问

假设真实文件位于 /srv/www/site/public/index.html。访问它时,进程需要对沿途各目录具备搜索权限,也就是目录上的 x;读取文件内容则需要文件的读权限。目录的 r 主要涉及列出目录内容,不能替代路径搜索权限。

可以先逐层查看,而不是直接递归改权限:

namei -l /srv/www/site/public/index.html
stat /srv/www/site/public/index.html
getfacl /srv/www/site/public/index.html
getfacl /srv/www/site/public

namei 和 getfacl 可能需要安装相应工具包。ACL 还可能包含影响有效权限的 mask,单看 ls -l 的三组权限位不一定完整。父目录也可能存在 ACL,需要按路径逐层核对。

把实际 worker 用户代入,可以做一个有限的身份检查:

# www-data 只是示例,请替换为实际 worker 用户。
sudo -u www-data test -r /srv/www/site/public/index.html
printf 'read-check-exit=%s\n' "$?"

退出状态为 0 表示该测试通过。这个检查主要辅助判断路径与传统访问权限;测试进程的安全上下文可能与 Nginx 不同,不能据此排除 SELinux、AppArmor、服务沙箱或容器挂载隔离。

如果 shell 中可读而 Nginx 仍报错,应回到日志确认文件是否相同,并检查符号链接、ACL、服务限制和安全策略。

权限修复要围绕实际访问需求

静态文件通常只需要让服务读取,上传目录或缓存目录才可能需要写入。两类路径应分开授权。把整个站点交给 Nginx 写入,会扩大应用漏洞可能造成的影响。

常见的目录 755、文件 644 只适用于允许其他本地账户读取内容的场景。私有文件、多租户服务器和包含凭据的目录不应机械套用。使用专用组或精确 ACL 时,应先核实服务的有效组和既有部署策略。

修改前记录目标对象的属主、组、模式和 ACL,确认影响范围,只修改确有问题的路径。如果原有 ACL 需要保留,可通过现有运维流程备份。组成员变更也未必会被正在运行的进程立即采用,需要按维护计划重新启动相应进程并核验。

不要执行 chmod -R 777。它可能让无关账户获得写权限,也不会绕过 SELinux。整个网站递归 chown 给 Web 用户同样不适合作为通用修复,会改变发布、备份和密钥文件的权限边界。

SELinux 开启时,核对标签与拒绝记录

在使用 SELinux 的系统中,传统权限满足后,强制访问控制仍可能拒绝访问。先检查状态、文件标签和近期审计记录:

getenforce
ls -Zd /srv /srv/www /srv/www/site /srv/www/site/public
ls -Z /srv/www/site/public/index.html
sudo ausearch -m AVC,USER_AVC -ts recent

工具是否存在、命令可用性和审计日志位置取决于发行版及安装情况。没有匹配的 AVC 记录也不能直接断言 SELinux 无关,应考虑审计服务状态、时间范围和日志访问权限。

自定义站点目录可能没有符合 Web 服务用途的标签。在采用常见 SELinux Web 服务策略的发行版上,静态内容常见类型为 httpd_sys_content_t,但具体标签与规则仍应以本机策略为准。写入目录需要额外评估,不应把整个站点都标记成可写内容。

下面是为一个已确认的静态目录添加持久化标签映射的示例,仅适用于管理员已核对策略和目标范围的环境:

# 会修改 SELinux 文件上下文规则与目录标签。
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www/site/public(/.*)?'
sudo restorecon -Rv /srv/www/site/public

执行前用 semanage fcontext -l 和 semanage fcontext -l -C 检查已有规则,记录原有映射。已经存在的映射可能需要调整,不能遇到报错就重复添加。restorecon -nRv 可用于预览预计变更;真正执行前还应确认目录内是否存在需要独立标签的子目录。

chcon 改动可能被后续标签恢复覆盖,长期配置宜使用持久化映射。回滚时应根据记录恢复或移除本次新增规则,再恢复相应标签,不要删除其他业务依赖的映射。

不建议为了网站恢复而全局关闭 SELinux,也不要把自动生成的宽泛允许规则当成默认答案。网络代理连接被拒绝时,需要单独核对相应策略与布尔值,和静态文件标签分开处理。

报错对象是 PHP-FPM socket 时

如果日志指向 /run/php/...sock,网站文件权限通常不是当前故障点。应检查 Nginx worker 能否访问 socket 的父目录,以及 PHP-FPM 池的 listen.owner、listen.group、listen.mode 和可能存在的 ACL 设置。

有些发行版通过服务配置或 ACL 管理 socket 权限。手动 chmod 即使短暂有效,也可能在 PHP-FPM 重建 socket 后丢失。应在负责创建 socket 的配置中修复,并安排受控的配置测试与服务操作。

不要把 socket 改为任意用户可连接,也不要为了绕过 socket 权限临时把 FPM 端口暴露到公网。如果有 SELinux AVC 记录,应同时核对 socket 标签和进程域。

修好后用原来的请求验收

验证时使用原故障的域名、路径和协议。通过 IP 访问可能落入其他虚拟主机;带登录态的浏览器也可能走不同路径。请求如果经过 CDN,要区分缓存响应与源站结果,避免把旧缓存当成修复失败或成功。

验收应包含:原失败资源恢复访问、同一时间错误日志没有继续出现相同拒绝、私有文件仍不可公开读取。涉及 socket 或部署流程的变更,还应在测试环境验证相关服务重启后权限是否保持。

只改文件权限通常不需要重新加载 Nginx;修改 Nginx 配置则应先通过语法检查,再按现有服务管理方式重载。无关的反复重启会增加影响,也容易丢失排查线索。

把最后的修复记录写清楚:哪个请求命中了哪个路径,worker 使用什么身份,被哪层检查拒绝,修改了哪个对象,如何恢复原设置。以后再迁移目录或升级服务,便有可对照的依据。

参考资料

核验日期:2026-11-02。以下资料用于核对指令、路径解析和 SELinux 标签工具行为;实际权限策略应结合发行版文档与本机配置确认。

Web服务与建站

网站返回 200 却被搜索引擎当成错误页?软 404、状态码与页面内容检查

2026-10-29 11:33:24

Web服务与建站

WordPress 修改后预览正常,访客却看不到?页面缓存与登录态差异排查

2026-11-2 10:18:49