核心是用try_files $uri $uri/ /index.html;实现spa路由兜底,优先匹配静态文件和目录,失败后内部重写至index.html由前端接管;需前置配置api代理和静态资源location避免误捕获。

在 Nginx 中为单页应用(SPA)配置路由,核心是让所有非静态资源的请求都 fallback 到 index.html,同时保留真实文件和 API 路径的正常处理。直接在 location 块中混用 try_files 和 rewrite 容易引发循环、重复重写或 404,关键在于理解二者执行顺序与作用域——try_files 是“尝试路径并终止当前 location”,而 rewrite 默认是内部重定向(除非加 redirect 或 permanent),可能触发新 location 匹配。
优先用 try_files 实现 SPA fallback(推荐)
绝大多数 SPA(如 Vue Router history 模式、React Router v6+)只需一条 try_files 就能干净解决:
- 匹配所有前端路由路径(如
/dashboard/user/123),先检查对应静态文件或目录是否存在 - 不存在时,内部重写到
/index.html,由前端 JS 路由接管 - 不触发外部跳转,URL 地址栏不变,SEO 和历史记录友好
示例配置:
location / {
try_files $uri $uri/ /index.html;
}
⚠️ 注意:$uri/ 会触发对目录的检查(如访问 /assets/ 时找 assets/index.html),若不需要目录索引,可去掉;若项目有真实子目录需保留,这条仍安全。
需要 rewrite 的典型场景及正确写法
当必须改写 URL 结构(如统一前缀、移除版本号、兼容旧路由)时,rewrite 才必要。但要避免与 try_files 冲突:
-
不要在同一个 location 中先 rewrite 再 try_files:rewrite 后若未加
last,Nginx 可能继续执行后续指令,导致逻辑混乱;加last又会重启 location 匹配,可能绕过你期望的 fallback - 正确做法:用独立 location 拦截需重写的路径,内部重定向后交由主 location 处理
例如,将带 /v1/ 前缀的路由剥离后再走 SPA 流程:
location ^~ /v1/ {
rewrite ^/v1/(.*)$ /$1 last;
}
location / {
try_files $uri $uri/ /index.html;
}
这里 ^~ /v1/ 优先匹配,rewrite ... last 触发新 location 查找,最终落入 location / 并执行 try_files,整个过程无外部跳转、无循环。
排除 API 和静态资源,避免误 fallback
SPA 的 try_files 必须避开 API 接口和真实静态文件,否则请求 /api/users 也会返回 index.html:
- 为 API 单独配置 location,用
proxy_pass转发,且放在location /之前(Nginx 按顺序匹配) - 显式声明静态资源路径(如
/assets/、/images/),用try_files直接返回,不 fallback
完整示例:
location /api/ {
proxy_pass http://backend;
proxy_set_header Host $host;
}
location /assets/ {
alias /var/www/myapp/dist/assets/;
try_files $uri =404;
}
location / {
root /var/www/myapp/dist;
try_files $uri $uri/ /index.html;
}
调试技巧:确认实际匹配与重写路径
遇到 fallback 失效或 404,开启 Nginx debug 日志最直接:
- 临时在
nginx.conf的http块中加:error_log /var/log/nginx/debug.log debug; - 重启 Nginx,复现请求,查看日志中 “using configuration”、“rewritten data”、“trying file” 等关键词
- 特别注意
rewrite是否被标记为using named location或redirect,这决定是否跳出当前流程
也可用 curl -I 检查响应头中的 X-Redirect-By(若自定义)或状态码,快速区分是内部重写还是外部跳转。











