ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Nginx 403 Forbidden 排查全链路:从来源定位到配置修复

Nginx 403 Forbidden 排查全链路:从来源定位到配置修复 1. 403 不是权限不够四个字能打发的但凡折腾过 Web 服务的人都见过那个冷冰冰的403 Forbidden。它不像 502 那样一看就知道是后端挂了也不像 404 那样明摆着是路径写错了。403 的讨厌之处在于服务器听懂了你的请求但就是不给你。至于为什么不给Nginx 默认只回一句forbidden多一个字都没有。我见过太多人遇到 403 的第一反应是去改chmod 777把整个目录权限拉满结果问题没解决还埋了个安全隐患。也见过有人反复重启 Nginx重启了七八次日志都不看一眼。这篇文章就是写给这些场景的——不管你是刚接触 Nginx 的新手还是被某个诡异 403 卡了半天的老手我都会把 403 的排查链路从头到尾拆一遍告诉你每一步该看什么、为什么看这个、看完之后怎么判断。核心关键词就几个Nginx、403、排查、解决。但真正有价值的是排查的顺序和判断依据而不是背几条命令。下面我按先定位再动手的思路把整个流程讲透。2. 先搞清楚 403 到底是谁返回的很多人一看到 403 就默认是 Nginx 的锅其实不一定。403 可能来自三个不同的地方排查方向完全不同。搞错来源后面全是白费功夫。2.1 三种 403 来源的区分方法第一种Nginx 自己返回的 403。这是最常见的情况通常是文件权限、目录索引、访问控制规则导致的。特征是响应头里带Server: nginx而且响应体往往是 Nginx 的默认错误页。第二种后端应用返回的 403。比如你用 Nginx 反代了一个 Java 或 Python 服务后端框架Spring Security、Django 等自己做了权限校验返回 403。这种情况下 Nginx 只是转发员真正的决策者是后端。第三种上游网关或 CDN 返回的 403。如果请求经过了 CDN、WAF 或云负载均衡它们可能基于 IP、地域、UA 等规则拦截返回 403。区分方法很直接用curl看响应头curl -I http://your-domain.com/path重点看两个东西Server头告诉你谁在处理请求响应体的内容能进一步确认。如果响应体是 Nginx 默认的 HTML 错误页基本就是 Nginx 自己返回的如果是 JSON 格式的错误信息多半是后端应用返回的。提示加-v参数可以看到完整的请求和响应过程包括 Nginx 是否做了内部跳转。这一步能帮你排除掉很多干扰。2.2 为什么必须先定位来源我踩过最典型的一个坑一个项目的静态资源一直 403我花了半小时查 Nginx 配置和文件权限最后发现是 CDN 那边配了个防盗链规则把不带 Referer 的请求全拦了。如果一开始就用curl直连源站 IP 测试五分钟就能定位。所以排查的第一步永远是绕过所有中间层直接请求源站。用curl --resolve或者直接指定 IPcurl -I http://源站IP/path -H Host: your-domain.com如果直连源站正常说明问题在中间层如果直连也 403那才是 Nginx 或后端的问题。这一步能帮你砍掉一半的排查范围。2.3 日志是唯一不会骗你的东西定位来源之后下一步就是看日志。Nginx 的error_log会明确记录 403 的原因比如directory index of ... is forbidden、access forbidden by rule、permission denied。这些信息比任何猜测都靠谱。tail -f /var/log/nginx/error.log一边刷新页面一边看日志403 产生的瞬间日志就会打出来。如果 error_log 里什么都没有那说明请求根本没到 Nginx问题在更上游。3. 文件权限与目录索引最常见的两个坑确认是 Nginx 自己返回的 403 之后接下来按概率排查。根据我的经验文件权限问题和目录索引未开启这两类占了 403 的七成以上。3.1 权限问题的本质Nginx 以谁的身份读文件Nginx 的 worker 进程是以某个用户身份运行的通常是nginx、www-data或nobody。这个用户必须对目标文件有读权限对文件所在的每一级目录有执行权限。注意是每一级目录缺一级都不行。很多人只改了文件本身的权限忘了父目录。比如/var/www/site/static/img/logo.pngNginx 要读到这个文件需要对/var/www、/var/www/site、/var/www/site/static、/var/www/site/static/img这四级目录都有x权限。任何一级缺失都会 403。查看 Nginx 运行用户ps aux | grep nginx输出里worker process前面的用户名就是。然后逐级检查权限namei -l /var/www/site/static/img/logo.pngnamei -l这个命令会把路径上每一级的权限都列出来一眼就能看出哪一级断了。这比手动ls -l一层层看高效得多。正确的权限设置通常是目录755文件644属主是 Nginx 用户或者至少对 Nginx 用户可读。不要用 777那等于把门拆了。3.2 目录索引为什么访问目录会 403当你访问一个目录路径比如http://site.com/images/而不是具体文件时Nginx 默认会尝试返回该目录下的索引文件index.html等。如果目录下没有索引文件而autoindex又是关闭的Nginx 就返回 403。日志里会明确写directory index of /path/ is forbidden。解决办法有两个要么在目录下放一个index.html要么开启目录列表location /images/ { autoindex on; autoindex_exact_size off; autoindex_localtime on; }autoindex_exact_size off会把文件大小显示成 KB/MB 而不是字节数autoindex_localtime on用本地时间显示这两个参数纯粹是为了可读性建议都加上。注意autoindex on会把目录下所有文件名暴露出来生产环境慎用。如果只是临时排查用完记得关掉。3.3 SELinux那个让你怀疑人生的隐藏开关如果你在 CentOS、RHEL 或 AlmaLinux 上权限和配置都查了没问题但就是 403那大概率是 SELinux 在作祟。SELinux 的安全上下文和普通文件权限是两套独立的机制chmod改的是后者前者不改照样 403。快速验证是不是 SELinux 的问题getenforce如果返回Enforcing临时设为宽容模式测试setenforce 0如果设完之后 403 消失了那就确认是 SELinux。但不要就这么算了正确做法是给文件打上正确的上下文标签chcon -R -t httpd_sys_content_t /var/www/site或者用semanage fcontext做持久化配置。临时关 SELinux 只是验证手段不是解决方案。4. 配置层面的 403规则、路径与重写权限和 SELinux 都排除了接下来就要看 Nginx 配置本身。配置导致的 403 往往更隐蔽因为规则是你自己写的你潜意识里觉得我这么写没问题。4.1 allow/deny 规则的匹配顺序Nginx 的allow和deny是按书写顺序从上到下匹配的匹配到第一条就停止。很多人写反了顺序导致本该放行的请求被拦。location /admin/ { deny 192.168.1.100; allow all; }上面这段是先拒绝特定 IP再放行其他所有。如果写反了location /admin/ { allow all; deny 192.168.1.100; }那allow all会先匹配deny永远不生效。反过来如果你只想放行内网、拒绝其他location /admin/ { allow 192.168.0.0/16; allow 10.0.0.0/8; deny all; }这个顺序是对的先放行内网段最后deny all兜底。日志里会显示access forbidden by rule看到这个就知道是 allow/deny 的问题。4.2 root 与 alias 的路径拼接差异root和alias的区别是 Nginx 新手最容易搞混的点也是 403 的高发区。root是拼接最终路径 root 值 location 匹配的 URI。alias是替换最终路径 alias 值 location 之后的部分。举个例子location /static/ { root /var/www/site; }访问/static/logo.png实际找的是/var/www/site/static/logo.png。location /static/ { alias /var/www/site/assets/; }访问/static/logo.png实际找的是/var/www/site/assets/logo.png。用alias时location 末尾和 alias 末尾的斜杠必须对应好否则路径会拼错拼到一个不存在的目录自然 403 或者 404。我建议排查时直接在配置里加一行return 200 $request_filename;临时打印出实际路径确认路径对不对比猜快得多。4.3 try_files 与内部重写导致的意外 403try_files用不好也会 403。比如location / { try_files $uri $uri/ 404; }如果$uri/匹配到了一个目录但该目录没有索引文件且autoindex关闭就会 403 而不是 404。这种情况下把$uri/去掉或者确保目录下有索引文件。还有一种情况是内部重写到了受保护的 location。比如rewrite把请求转到了/admin/路径而/admin/配了deny all那最终结果就是 403。排查时看 error_log 里的internal redirect相关记录能追踪到重写后的最终路径。5. 反向代理场景下的 403问题可能不在 Nginx用 Nginx 做反向代理时403 的来源就更复杂了。Nginx 本身可能没问题但后端应用、代理头、协议协商任何一个环节出问题都会表现为 403。5.1 代理头缺失导致的 403很多后端框架会校验Host、X-Forwarded-For、X-Real-IP等头部。如果 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; }这四个proxy_set_header是标配建议所有反代配置都加上。特别是X-Forwarded-Proto如果后端做了 HTTPS 强制校验缺了这个头会直接 403。5.2 后端应用自身的权限校验如果后端是 Spring Security、Django、Express 这类带权限框架的应用403 很可能是它们返回的。这时候 Nginx 的 error_log 里不会有记录因为请求成功转发到了后端是后端返回的 403。判断方法看响应体。后端返回的 403 通常带 JSON 或框架特定的错误页而不是 Nginx 的默认页。这时候要去查后端的日志而不是 Nginx 的。我遇到过一个案例Nginx 配置完全正确但后端接口一直 403。最后发现是后端的 CORS 配置里没加某个请求头浏览器预检请求被拒。这种问题在 Nginx 层面怎么查都查不出来必须看后端日志。5.3 协议与端口不匹配的隐蔽问题还有一种情况Nginx 用 HTTP 转发到后端的 HTTPS 端口或者反过来。后端收到协议不匹配的请求可能返回 403。proxy_pass https://127.0.0.1:8443; proxy_ssl_server_name on;如果后端是 HTTPSproxy_pass必须用https://而且可能需要proxy_ssl_server_name on来正确发送 SNI。这些细节不处理好403 就来了。6. 一套可复用的 403 排查流程讲了这么多场景最后我把整个排查流程串成一条线。下次再遇到 403按这个顺序走基本不会漏。6.1 排查步骤清单步骤操作判断依据1curl -I看响应头确认 Server 头判断 403 来源2直连源站 IP 测试排除 CDN/WAF 干扰3tail -f error.log看 Nginx 是否记录 403 原因4namei -l查路径权限逐级确认读/执行权限5getenforce查 SELinux确认是否安全上下文问题6检查 allow/deny 规则确认匹配顺序是否正确7确认 root/alias 路径用return 200 $request_filename验证8检查代理头配置确认后端所需头部是否齐全9查后端日志确认是否后端应用返回的 403这个顺序的核心逻辑是从外到内从简到繁。先排除中间层再排除 Nginx 自身最后查后端。每一步都有明确的判断依据不靠猜。6.2 几个容易忽略的细节日志级别不够。默认的error_log级别是error有些 403 的详细信息在info或debug级别才会打出来。临时调高error_log /var/log/nginx/error.log debug;排查完记得调回去debug 级别日志量很大会拖慢性能。配置没重载。改完配置一定要nginx -t测试语法然后nginx -s reload重载。我见过有人改了配置没重载查了半天以为是配置问题其实是旧配置还在跑。浏览器缓存。有时候 403 是浏览器缓存的旧响应换个浏览器或者用curl测试就能排除。这个坑很蠢但很常见。文件系统挂载选项。如果网站目录是挂载的 NFS 或其它网络文件系统挂载选项里的noexec、nosuid等可能影响访问。检查mount输出确认。6.3 我个人的经验总结排查 403 这么多年我最大的体会是不要跳过验证步骤。很多人看到 403 就直接去改权限改完发现没用再改配置改完还是没用最后才想起来看日志。如果一开始就看日志可能五分钟就解决了。另一个体会是记录每次排查的过程。我现在遇到 403会先在纸上写下来源判断→日志确认→权限检查→配置检查→后端检查这条链路每查完一步打个勾。这样不会漏也不会重复查。时间长了这套流程就变成肌肉记忆了。最后说一个反直觉的点有时候 403 是故意设计的。比如某些 API 对未授权请求返回 403 而不是 401这是业务逻辑的一部分不是 bug。遇到这种情况先确认预期行为是什么再判断是不是真的有问题。别把正常行为当成故障来修。
返回列表