ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Nginx 403 Forbidden 排查指南:权限、SELinux 与配置详解

Nginx 403 Forbidden 排查指南:权限、SELinux 与配置详解 1. Nginx 403 报错到底卡在哪一环Nginx 返回 403 Forbidden本质就一句话服务器听懂了你的请求但拒绝给你对应的资源。这跟 404 完全是两码事——404 是“你要的东西我这儿压根没有”403 是“东西在但我不给你”。很多刚接触 Nginx 的朋友一看到 403 就慌觉得是不是配置全崩了其实绝大多数情况下问题就出在三个地方文件权限、目录索引、访问控制规则。我这些年帮人排查 Nginx 403粗略统计下来权限问题占六成autoindex没开占两成剩下两成是deny、allow规则或者 SELinux 在捣鬼。你只要按顺序把这几条线捋一遍基本都能定位到根因。这篇文章面向的是所有被 Nginx 403 卡住的人——不管你是刚在 Linux 上装完 Nginx 想放个静态页面还是用 Docker 跑 Nginx 做反向代理又或者是用 OpenResty 做网关时突然遇到 403下面的排查思路和实操步骤都能直接拿去用。我会从权限模型讲到配置细节再给出一套完整的排查流程和常见坑位速查表尽量让你看完就能动手解决。提示排查 403 之前先确认 Nginx 的错误日志路径。默认在/var/log/nginx/error.logDocker 环境下通常在容器内同路径或者通过docker logs查看。日志里会直接告诉你Permission denied还是directory index of ... is forbidden这是最快的突破口。2. 权限模型与访问控制的核心逻辑2.1 Linux 文件权限如何影响 NginxNginx 的 worker 进程通常以www-dataDebian/Ubuntu或nginxCentOS/RHEL身份运行。你可以通过ps aux | grep nginx看到类似这样的输出root 1234 0.0 0.1 12345 6789 ? Ss 10:00 0:00 nginx: master process /usr/sbin/nginx www-data 1235 0.0 0.1 12456 7890 ? S 10:00 0:00 nginx: worker process关键点在于worker 进程能不能读到你的文件取决于文件路径上每一级目录的执行权限x和文件本身的读权限r。很多人只改了文件权限chmod 644 index.html却忘了目录没有x权限结果照样 403。我举个实际例子。假设你的站点根目录是/var/www/mysite结构如下/var/www/mysite/ ├── index.html └── assets/ └── style.css要让 Nginx 能读到index.html必须满足/var目录对www-data有x权限/var/www目录对www-data有x权限/var/www/mysite目录对www-data有x权限index.html文件对www-data有r权限任何一级断了都会 403。你可以用namei -l /var/www/mysite/index.html这条命令一次性列出路径上所有层级的权限非常直观namei -l /var/www/mysite/index.html输出会像这样f: /var/www/mysite/index.html drwxr-xr-x root root / drwxr-xr-x root root var drwxr-xr-x root root www drwxr-xr-x root root mysite -rw-r--r-- root root index.html如果某一级目录的 others 权限没有xNginx 就进不去。常见的修复方式是chmod 755 /var/www /var/www/mysite chmod 644 /var/www/mysite/index.html但要注意不要图省事直接chmod -R 777这在生产环境是严重的安全隐患等于把服务器大门敞开。正确的做法是让目录属于www-data组然后设置合理权限chown -R root:www-data /var/www/mysite chmod -R 750 /var/www/mysite chmod 640 /var/www/mysite/index.html这样www-data组有读和执行权限其他用户无权访问安全性和可用性兼顾。2.2 SELinux 与 AppArmor 的隐形拦截如果你在 CentOS、RHEL、Fedora 或者 AlmaLinux 上跑 Nginx权限全对但依然 403那大概率是SELinux在拦截。SELinux 的安全上下文跟普通文件权限是两套独立机制ls -l看着没问题但 SELinux 策略不允许 Nginx 读那个目录。快速判断方法getenforce如果返回Enforcing那就要检查文件的安全上下文ls -Z /var/www/mysite/index.html正常应该是httpd_sys_content_t类型。如果不是用chcon或semanage修复chcon -R -t httpd_sys_content_t /var/www/mysite或者更规范地semanage fcontext -a -t httpd_sys_content_t /var/www/mysite(/.*)? restorecon -Rv /var/www/mysiteUbuntu/Debian 用的是 AppArmor相对宽松一些但如果你自定义了 Nginx 的配置路径也可能被拦。检查/etc/apparmor.d/usr.sbin.nginx里是否包含了你的站点目录。注意临时用setenforce 0关闭 SELinux 可以验证问题但生产环境不建议长期关闭正确做法是调整策略或安全上下文。2.3 Nginx 自身配置中的访问控制指令Nginx 配置文件里有几个指令会直接导致 403deny all;—— 明确拒绝所有访问allow/deny组合 —— 按 IP 或网段控制auth_basic—— 开启认证但未提供正确凭据时返回 401配置不当也可能表现为 403autoindex off;—— 目录下没有索引文件且未开启目录列表时返回 403一个典型的误配置场景location /admin { deny all; }这会让/admin下所有请求直接 403。如果你确实想限制访问应该配合allow使用location /admin { allow 192.168.1.0/24; deny all; }另外root和alias指令用错也会导致 403。root是拼接路径alias是替换路径。比如location /static/ { alias /var/www/static/; }请求/static/logo.png会映射到/var/www/static/logo.png。如果用root /var/www/static;则会映射到/var/www/static/static/logo.png路径不存在自然 403 或 404。3. 从日志到配置的完整排查流程3.1 第一步看错误日志定位方向任何时候排查 403第一件事都是看日志。执行tail -f /var/log/nginx/error.log然后在浏览器里复现 403 请求日志里会立刻出现对应记录。常见的几种日志信息及含义日志内容含义排查方向Permission denied文件系统权限不足检查目录和文件权限、SELinuxdirectory index of ... is forbidden目录无索引文件且未开 autoindex添加 index 文件或开启 autoindexaccess forbidden by rule被 deny 规则拦截检查 allow/deny 配置client denied by server configuration配置层面拒绝检查 location 匹配和 auth 配置Docker 环境下如果 Nginx 跑在容器里日志可能在容器内docker logs nginx-container --tail 50或者进入容器查看docker exec -it nginx-container tail -f /var/log/nginx/error.log3.2 第二步确认 Nginx 运行用户与文件归属查看 Nginx 主进程和 worker 进程的用户ps aux | grep nginx查看 Nginx 配置中指定的用户grep -r ^user /etc/nginx/nginx.conf默认情况下Ubuntu 是www-dataCentOS 是nginx。然后检查你的站点文件归属ls -la /var/www/mysite/如果文件属于root:root且权限是600那 Nginx 肯定读不了。修复方式前面已经讲过核心原则是让 Nginx 运行用户对路径有读和执行权限但不要给过大的权限。3.3 第三步逐级验证路径可达性用sudo -u模拟 Nginx 用户去读文件这是最直接的验证方法sudo -u www-data cat /var/www/mysite/index.html如果报Permission denied说明权限确实有问题。如果这条命令能正常输出内容但浏览器还是 403那问题就不在文件权限而在 Nginx 配置或 SELinux。再进一步用namei检查每一级namei -l /var/www/mysite/index.html确保每一级目录都有x权限给到 Nginx 用户或所属组。3.4 第四步检查 Nginx 配置中的 root/alias 与 index打开你的站点配置文件通常在/etc/nginx/conf.d/或/etc/nginx/sites-enabled/下。重点看server { listen 80; server_name example.com; root /var/www/mysite; index index.html index.htm; location / { try_files $uri $uri/ 404; } }确认root指向的目录真实存在且index指定的文件在目录里存在。如果目录下没有任何 index 文件而你又没开autoindexNginx 就会返回 403。临时开启目录列表用于调试location / { autoindex on; }改完执行nginx -t测试配置然后nginx -s reload重载。如果开启后能列出目录说明之前就是缺索引文件。3.5 第五步排查反向代理场景下的 403反向代理时出现 403原因又不一样了。比如你用 Nginx 代理后端服务后端返回 403那问题可能在后端而不是 Nginx。这时候要看 Nginx 的proxy_pass配置和请求头传递。一个常见问题是后端服务校验Host头或Origin头Nginx 默认转发的头不符合后端要求。可以这样调整location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }如果后端是对象存储或 CDN 回源403 可能是签名或 Referer 校验问题需要检查proxy_set_header Referer是否正确传递。还有一种情况是 Nginx 作为静态资源服务器但请求的 URL 里包含了特殊字符或路径穿越尝试Nginx 会直接 403。比如请求/../etc/passwdNginx 会拒绝。这是正常的安全行为不用修。4. 高频 403 场景与速查解决表4.1 静态站点部署后的 403这是最经典的场景装完 Nginx把网页文件放到/var/www/html浏览器一访问就 403。原因通常是文件权限不对 —— 用chmod 644和chmod 755修复目录下没有index.html—— 放一个进去或者开启autoindexSELinux 拦截 —— 用chcon修复上下文Nginx 配置的root路径写错 —— 核对路径我整理了一个速查表遇到 403 时按顺序过一遍检查项命令期望结果Nginx 运行用户ps aux | grep nginx确认 worker 用户文件权限ls -la /path/to/file至少有 r 权限给对应用户目录权限namei -l /path/to/file每级目录有 x 权限SELinux 状态getenforce若 Enforcing 则检查上下文安全上下文ls -Z /path/to/file应为 httpd_sys_content_t索引文件ls /path/to/dir/存在 index.html 或 index.htmautoindexgrep autoindex /etc/nginx/...按需开启deny 规则grep -r deny /etc/nginx/确认没有误拦截配置语法nginx -t语法正确错误日志tail /var/log/nginx/error.log查看具体拒绝原因4.2 Docker 环境下的 Nginx 403Docker 里跑 Nginx403 的根源往往是挂载卷的权限映射问题。容器内的 Nginx 用户 UID 和宿主机上的文件 UID 不一致导致容器内进程读不了挂载进来的文件。比如你把宿主机的/home/user/site挂载到容器的/usr/share/nginx/html宿主机上这个目录属于 UID 1000 的用户而容器内 Nginx 用户 UID 是 101nginx用户。容器内进程以 UID 101 运行对 UID 1000 的文件没有读权限就 403 了。解决方法有几种在宿主机上把文件所属组改为容器内 Nginx 用户的 GID并给组读权限构建自定义镜像时调整 Nginx 用户 UID 与宿主机一致用docker run --user指定运行用户我一般推荐第一种操作简单# 查看容器内 nginx 用户的 UID 和 GID docker exec nginx-container id nginx # 假设输出 uid101(nginx) gid101(nginx) # 在宿主机上调整 chown -R 1000:101 /home/user/site chmod -R 750 /home/user/site这样容器内 GID 101 的用户就能读了。另外Docker 的nginx官方镜像默认配置文件在/etc/nginx/conf.d/default.conf如果你挂载了自己的配置注意检查root路径是否指向容器内实际存在的目录。4.3 反向代理与 API 网关场景的 403用 Nginx 做反向代理时403 可能来自三个层面Nginx 自身规则拦截—— 检查deny、auth_basic、limit_except后端服务返回 403—— 查看后端日志确认是认证失败还是权限不足中间网络设备拦截—— 比如 WAF、防火墙对特定请求头或路径的拦截排查时可以先绕过 Nginx直接用curl请求后端curl -v http://127.0.0.1:8080/api/test如果后端直接返回 403那问题在后端。如果后端正常但经过 Nginx 就 403那就在 Nginx 配置里找原因。一个容易忽略的点是proxy_pass的路径拼接。比如location /api/ { proxy_pass http://backend/api/; }请求/api/user会被转发到http://backend/api/user。如果后端只接受/user就会 403 或 404。这时候要去掉proxy_pass末尾的路径location /api/ { proxy_pass http://backend/; }请求/api/user就变成http://backend/user。4.4 OpenResty 与 Lua 层面的 403OpenResty 基于 Nginx 加了 Lua 支持403 可能来自 Lua 代码里的ngx.exit(403)或ngx.status 403。排查时要看 Lua 脚本里的访问控制逻辑比如 token 校验、IP 白名单、频率限制等。常见的是 token 校验失败返回 403local token ngx.req.get_headers()[Authorization] if not token or token ~ expected_token then ngx.exit(403) end这种情况下Nginx 错误日志里可能没有明显记录需要看 OpenResty 自己的日志或 Lua 脚本里的ngx.log输出。5. 实操心得与避坑清单5.1 权限修复的正确姿势我见过太多人一遇到 403 就chmod -R 777这在测试环境图快可以理解但生产环境绝对不能这么干。正确的权限设置应该是目录755或750确保 Nginx 用户有x权限文件644或640确保 Nginx 用户有r权限归属root:nginx或deploy:www-data让 Nginx 用户通过组权限访问如果站点需要上传功能上传目录可以给写权限但不要给执行权限chmod 770 /var/www/mysite/uploads chown www-data:www-data /var/www/mysite/uploads这样 Nginx 用户能读写但不能执行里面的脚本降低安全风险。5.2 配置修改后的标准操作每次改完 Nginx 配置必须执行两步nginx -t nginx -s reloadnginx -t会检查语法如果有错会直接告诉你哪一行有问题。不要跳过这一步直接 reload否则配置错误可能导致 Nginx 无法正常服务。如果nginx -t报错说找不到某个文件或目录先确认路径是否存在再检查权限。有时候是配置文件里引用了不存在的 SSL 证书路径也会导致启动失败。5.3 日志级别调整技巧默认的error_log级别是error有些 403 的细节可能看不到。临时调成debug可以看到更详细的信息error_log /var/log/nginx/error.log debug;改完 reload 后复现请求日志里会输出权限检查的详细过程。排查完记得改回error或warn否则日志量会非常大磁盘很快被占满。5.4 常见误区澄清误区一403 就是权限问题。不一定autoindex off加无索引文件也会 403deny规则也会 403。误区二改了文件权限就够了。目录的执行权限同样重要路径上任何一级目录没有x权限都会 403。误区三SELinux 没用可以关掉。关掉确实能解决 403但会降低系统安全性。正确做法是调整安全上下文或策略。误区四Docker 里 403 是 Nginx 配置问题。很多时候是挂载卷的 UID/GID 映射问题跟 Nginx 配置无关。误区五重启 Nginx 能解决 403。重启只能让配置生效如果配置本身有问题重启多少次都一样。5.5 一个完整的排查案例最后分享一个我最近处理的案例。用户反馈在 AlmaLinux 9 上装完 Nginx把网页放到/web/site目录访问一直 403。排查过程如下看错误日志Permission denied说明是权限问题namei -l /web/site/index.html发现/web目录权限是700只有 root 能进修复chmod 755 /webchmod 755 /web/sitechmod 644 /web/site/index.html再次访问还是 403getenforce返回EnforcingSELinux 开着ls -Z /web/site/index.html上下文是default_t不是httpd_sys_content_t修复semanage fcontext -a -t httpd_sys_content_t /web/site(/.*)?然后restorecon -Rv /web/site访问正常这个案例典型地展示了 403 排查的完整链路日志定位方向权限检查排除文件系统问题SELinux 检查排除安全模块拦截。三步走下来基本没有解决不了的 403。提示如果你在排查过程中改了 SELinux 策略或文件上下文记得记录变更内容方便后续审计和回滚。生产环境的每一次变更都应该有据可查。5.6 预防 403 的日常习惯与其等 403 出现了再排查不如平时就养成好习惯部署新站点时先确认目录权限和归属再放文件修改 Nginx 配置后先nginx -t再 reload定期检查错误日志发现异常请求及时处理Docker 部署时提前确认容器内用户 UID/GID 与宿主机文件归属的映射关系使用配置管理工具如 Ansible统一管理权限设置避免手动操作遗漏这些习惯看起来简单但能帮你省下大量排查时间。我自己的服务器上每次新部署站点都会跑一个检查脚本自动验证权限、SELinux 上下文和 Nginx 配置语法确认无误后才正式上线。这个脚本不复杂但确实管用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进