Nginx 静态资源 404 与权限被拒排查:root、alias、try_files 的坑

    发布时间:2026-09-18 12:32 更新时间:2026-09-18 12:32 阅读量:0

    很多站长都遇到过这种场景:图片上传成功、文件明明躺在服务器上,改完 Nginx 配置、执行 nginx -s reload 之后,浏览器里访问图片还是 404。另一类更让人摸不着头脑——路径写对了,却返回 403 Forbidden,日志里写着 Permission denied。这两类问题的根源其实集中在三处:root 与 alias 的路径拼接规则、try_files 的兜底写法、以及目录权限(含 SELinux 上下文)。下面按排查顺序逐个讲清楚。

    root 会拼接 location,alias 直接替换

    这是最容易踩的坑。Nginx 的 root 指令会把 location 匹配到的路径原样拼接到 root 后面;而 alias 是把 location 匹配的部分整体替换掉。举个最常见的例子,你想把 /static/ 映射到磁盘上的 /data/www/static/:

    location /static/ {
        root /data/www;
    }
    

    访问 https://example.com/static/logo.png 时,Nginx 实际去找的文件是 /data/www/static/logo.png,因为 location 里的 /static/ 被拼到了 root 后面。而如果你写成 alias:

    location /static/ {
        alias /data/www/static/;
    }
    

    那么 /static/ 会被替换成 /data/www/static/,最终路径同样是 /data/www/static/logo.png。两种写法结果一样,但推导过程完全不同。坑就出在这里:有人把 root 当成 alias 用,写了 root /data/www/static;,结果 Nginx 去找 /data/www/static/static/logo.png,自然 404。

    判断方法很简单:用 nginx -T 打印完整生效配置,确认 location 与 root/alias 的对应关系。另外提醒两点,一是 alias 的路径结尾最好带斜杠,与 location 保持一致,否则容易出现路径错位;二是 alias 放在正则 location 里行为较特殊,具体以官方文档为准。还有一个细节,root 与 alias 不要写在同一个 location 里,Nginx 会报 “root directive is duplicate” 之类的配置错误,reload 直接失败,此时旧配置仍在运行,你以为改生效了其实没有。

    用 try_files 兜底,避免 SPA 与图片误 404

    排查完路径拼接,接下来要区分“文件真不存在”和“路由没匹配上”。单页应用(SPA)常见配置是:静态文件命中就返回,否则回落到 index.html。这里如果 try_files 写错,图片会被当成路由处理,返回 404 或空白页。正确写法示例:

    location / {
        root /data/www/site;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
    

    这段配置的含义是:先找 $uri 对应的文件,再找同名目录,都没有就交给 /index.html。注意 $uri 不包含查询字符串,所以 logo.png?v=2 也能正确命中。如果你的图片目录是独立的,比如 /uploads/,建议单独开一个 location 并显式返回 404,避免被兜底规则吞掉:

    location /uploads/ {
        root /data/www/site;
        try_files $uri =404;
    }
    

    当 try_files 找不到文件时,默认返回的是 Nginx 自己的 404,但如果最后一个参数是 =404,就明确返回 404 状态。这样配合 error_log 能更清楚地看到是哪个路径没找到。排查时打开 error_log 的 info 级别(临时调试用,生产环境记得调回 warn 或 error),日志里会提示 “open() ... failed (2: No such file or directory)”,这句话直接告诉你去磁盘上核对真实路径。

    权限被拒与 SELinux 的额外检查

    路径对了还报 403,问题多半在权限。Nginx 默认以 nginx 或 www-data 用户运行(取决于发行版),这个用户必须对文件本身和从根到文件的所有父目录都有可执行(x)权限,否则就算文件是 644 也读不到。父目录缺少 x 权限是最隐蔽的一种,日志会写 “Permission denied” 而不是“No such file”。

    排查步骤建议这样走:先确认 Nginx 运行用户,再逐级检查目录权限。命令如下:

    ps -o user= -p $(cat /run/nginx.pid)
    namei -l /data/www/site/uploads/logo.png
    

    namei -l 会从根目录开始逐级列出每一层的属主、属组和权限位,一眼就能看出哪一级缺 x 或不可读。修正常见做法是把目录设为 755、文件设为 644,属主给部署用户,属组给 Nginx 用户,例如:

    chown -R deploy:nginx /data/www/site
    find /data/www/site -type d -exec chmod 755 {} \;
    find /data/www/site -type f -exec chmod 644 {} \;
    

    如果权限看起来完全正常,但依旧 403,就要考虑 SELinux。在 CentOS、Rocky、AlmaLinux 等默认开启 SELinux 的系统上,文件除了传统权限,还要有正确的安全上下文。用 getenforce 看当前模式,用 ls -Z 看上下文:

    getenforce
    ls -Z /data/www/site/uploads/logo.png
    

    如果上下文不是 httpd_sys_content_t(例如是 user_home_t 或 default_t),Nginx 就会被 SELinux 拦下。临时验证可以执行 setenforce 0 切到宽容模式,如果 403 立刻消失,基本可确认是 SELinux 问题。但不建议长期关闭 SELinux,正确做法是给目录打上正确标签:

    semanage fcontext -a -t httpd_sys_content_t "/data/www/site(/.*)?"
    restorecon -Rv /data/www/site
    

    另外,/var/log/nginx/ 下的 error.log 与 /var/log/audit/audit.log 会分别记录 Nginx 报错和 SELinux 拒绝记录,遇到 403 时两个都值得看一眼。SELinux 的布尔值、标签类型在不同发行版上略有差异,具体以官方文档和实际环境为准。

    小结一下排查顺序:先看 error_log 里是 “No such file” 还是 “Permission denied”;前者回到 root/alias 与 try_files 检查路径拼接,后者用 namei 逐级查权限,再确认 SELinux 上下文;改完配置用 nginx -t 校验语法后再 reload,避免配置没生效还误以为改了没用。把这几步固化成检查清单,静态资源 404 和 403 基本都能在几分钟内定位。

    继续阅读

    📑 📅
    Linux网卡丢包与TCP重传排查:ip -s link、ss -ti与ethtool实战 2026-09-18
    journald 日志占满 /var/log:持久化与容量限制配置 2026-09-18
    Docker容器时区不对?TZ变量与localtime挂载的正确用法 2026-09-17
    服务器 swap 使用率飙升:正常换页还是内存真不够用 2026-09-17
    系统盘满了却找不到大文件:被删除但仍被进程占用的句柄排查与恢复空间 2026-09-17
    systemd-journald 日志转发远程 syslog:rsyslog 对接与丢日志排查 2026-09-19
    MySQL连接被拒绝Connection refused逐层排查思路 2026-09-19
    Docker容器DNS解析异常排查:resolv.conf与自定义网络 2026-09-19
    PHP-FPM 进程数怎么调:pm.max_children 与内存估算 2026-09-19
    Nginx gzip 与 Brotli 压缩配置实战:静态资源体积优化 2026-09-19