DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Nginx 反代路径下的 JS、CSS 被静态文件正则截走

反代前缀下只有 JS、CSS 请求落到 Nginx 本地目录时,核对同级扩展名正则是否覆盖普通前缀选择。用限定范围的 ^~ 保留资源代理,并验证原有权限与 URI 映射。

同一个后端路径,普通页面能通过 Nginx 访问,路径下的 .js、.css 却返回本地文件的 404。后端访问日志里没有这些资源请求,而 Nginx 错误日志尝试打开的路径落在另一套静态目录。这时应检查请求最终选中了哪个 location。

当同一层配置既有普通前缀反代,又有匹配扩展名的正则时,前缀匹配不一定是最终结果。下面讨论当前 Nginx 的同级、非嵌套 location;配置中还有嵌套块、精确匹配或内部重定向时,需要连同那些规则一起判断。

用实际加载的规则解释资源请求

例如,同一个 server 中存在以下结构。地址、目录和路径均为示例:

location /backend/service1/ {
    proxy_pass http://127.0.0.1:9000/service1/;
}

location ~* \.(js|css)$ {
    root /srv/site-static;
    expires 8d;
}

对 /backend/service1/assets/app.js,Nginx 先记住最长匹配的普通前缀,再检查同级正则。扩展名正则匹配成功,就采用那个块的处理配置,去本地静态目录找文件,而不会继续执行前面块里的反向代理。

把普通前缀块移到正则前面,不能改变这一点。正则块之间才按出现次序取第一个匹配项;不能把“配置写在前面”理解成所有 location 都优先。

在实际运行 Nginx 的环境中展开配置文件:

nginx -T

-T 从 Nginx 1.9.2 起提供,会校验配置并输出展开的配置文件。服务若使用自定义 -c 配置或 -p 前缀,这里也要保留相同参数;容器部署要检查容器里的实例。输出可能含内部地址和敏感配置,保存、分享前应脱敏。-T 读取的是磁盘配置,不能证明当前 worker 已加载它;还要核对最近一次加载结果与日志。

找到处理当前域名的 server,把 include 带进来的扩展名正则也算进去。对照失败请求的完整路径、Nginx 错误日志中的本地文件路径,以及后端同一时间的访问记录。后端没有日志本身不足以定因;结合本地文件查找记录和匹配规则,才支持“请求未进入代理块”的判断。静态块关闭了错误记录时,不要把没有错误日志当成排除依据。

如果后端确实收到请求并返回 404,应先查传给后端的 URI 和资源是否存在。此时给 location 加修饰符并不能补出缺失资源。

只保护需要转发的资源前缀

如果只有 /backend/service1/assets/ 下的资源必须交给后端,可以保留原普通反代和通用静态规则,新增一个更具体的同级块:

location /backend/service1/ {
    proxy_pass http://127.0.0.1:9000/service1/;
}

location ^~ /backend/service1/assets/ {
    proxy_pass http://127.0.0.1:9000/service1/assets/;
}

location ~* \.(js|css)$ {
    root /srv/site-static;
    expires 8d;
}

在这组同级规则中,请求 /backend/service1/assets/app.js 的最长匹配前缀带有 ^~,Nginx 不再进入该层正则匹配,资源交给后端。前缀末尾的 / 限定了目录边界,不会把 /backend/service1/assets-old/app.js 也纳入同一规则。

这是路由结构示例,合并时必须保留原代理需要的请求头、鉴权、限流和其他设置。新建的 location 不会继承旁边另一个 location 的配置;原设置写在普通反代块内时,需要按实际继承规则配置到新块,或整理为两个块共用的 include。

尤其检查被跳过的正则是否承担访问限制、敏感文件拦截等职责。不能仅为了让 JS 文件可访问,就让这一前缀失去原有保护。如果只有某个资源目录需要改变分流,就不要给整个站点的 location / 加 ^~。

这里的 proxy_pass 仍带有 URI:匹配到的 /backend/service1/assets/ 会被替换为 /service1/assets/,所以后端收到 /service1/assets/app.js。按原服务约定保留映射,不要顺手删除或增加代理地址末尾的斜杠。实际配置若原本不带 URI,或使用变量、rewrite,应单独核对其转发语义。

如果服务的整个前缀都必须走代理,也可以直接修改原前缀块,但要先检查该范围内所有原正则的用途。存在更长前缀、精确匹配或嵌套 location 时,不能仅看到某处写了 ^~ 就认为所有请求都会绕过整站正则;应重新走完那条请求的匹配和内部跳转过程。

检查资源归属,再重载配置

修改前备份当前配置及相关 include,在隔离环境确认规则范围。部署时先用运行实例对应的参数校验:

nginx -t

通过后按原服务管理方式重载。语法通过只证明配置可加载,不能证明资源已经交给正确的后端。

用已知存在的 JS、CSS 和普通页面分别发起正常 GET 请求,对照后端访问记录、响应正文与类型。资源即使返回 200,也可能是另一套静态目录中的同名文件,不能只看状态码。还应检查一个不属于保护前缀的静态资源仍由原规则处理,以及不存在的资源仍按应用约定失败。

带身份限制的路径要同时验证未授权请求仍被拒绝。如果改变规则后权限、缓存头或路由发生回归,恢复本次 location 与 include 修改,重新校验并重载。这个问题不需要搬动业务文件、扩大目录权限或删除静态资源。