nginx try_files + index 指令的两个坑(前端 SPA / 静态站点)
某 Astro 静态站点 vhost 在路径互换那次先后踩了两个 try_files 坑,值得记。
坑 1:try_files <static-html> =404 吞掉所有静态资源 MIME
最初的兜底:
location / {
try_files /admin/index.html =404;
}意图是”任何 URL 都返回 admin dashboard HTML”,/uses/、/projects/ 这种 public 子页就不会从 dist 漏出来。
问题:try_files 跟 URI 无关——它永远返回 /admin/index.html(因为这是字面常量,不是变量)。/admin/index.html 是个 .html 文件,nginx 按 MIME map 返 Content-Type: text/html。
于是 /_astro/authelia.CwygM0gi.css 这种被 Astro <link rel="stylesheet"> 引用的资产也被服成 text/html → 浏览器拒绝当 CSS 加载 → 整个页面无样式。
修法:把”必须按真实路径返”的资产路径显式 prefix-match,catchall 那条只兜不存在的路径:
location ^~ /_astro/ {
try_files $uri =404;
expires 30d;
}
location = /favicon.svg { try_files $uri =404; }
# 路由页面
location /authelia/ { try_files /authelia/index.html =404; }
location /guest/ { try_files /dashboard/index.html =404; }
# 真兜底
location / {
try_files /admin/index.html =404;
}^~ 告诉 nginx “这条 prefix 命中后不再 regex match”,优先级高于普通 prefix。
坑 2:index index.html + try_files $uri ... 让根目录漏出 dist 里的 public 主页
为了修坑 1 试过另一种写法:
index index.html;
location / {
try_files $uri $uri/ /admin/index.html;
}意图是”先试真实文件,找不到再回退到 admin”。
问题:Astro 自己 build 出 dist/index.html 是公开站主页,不是 admin。请求 / 进 location /:
try_files $uri→ URI 是/,展开成 root 目录- nginx 看到目录会用
index index.html指令查dist/index.html,找到 - 返回公开站主页
admin 入口看上去变成了公开介绍页,而且 /uses/、/projects/ 这些 public 子页也通过 $uri/ 漏出来。
修法:
- 删
index指令(避免目录自动找 index.html) - 在 catchall location
/里只 fallback 到 admin,不试$uri - 静态资源走
^~ /_astro/这种显式 prefix 兜住
总结规则
| 场景 | 写法 |
|---|---|
| 路由页面(SPA / 多页静态站) | location /path/ { try_files /path/index.html =404; } 字面常量,不靠 $uri |
| 静态资源(CSS/JS/字体/图标) | location ^~ /assets/ { try_files $uri =404; } 用 ^~ + $uri 按真实路径返 |
| 任何要 fall back 到 SPA 入口的兜底 | location / { try_files /spa/index.html =404; } 不要 $uri $uri/,会被同 dist 里的别的页面截胡 |
index 指令 | 慎用——会让目录请求自动找 index.html,跟 try_files 配合容易产生意料外的命中 |