Vue、React 单页应用刷新后 404 怎么办?Nginx History 路由与 API 分流

只让合法前端路由回退到入口文件,保留 API、静态资源和无效地址的真实状态

Vue、React 单页应用点击路由正常但刷新 404 时,先确认 SPA 部署模式,再配置受限的 Nginx try_files,并隔离 API、上传与静态资源。

首页能开、点链接正常,但刷新二级路由返回 Nginx 404,先检查单页应用的 History 回退:Vue Router 的 createWebHistory()、React Router Browser Router 等前端路由会在浏览器里切换地址;直接刷新 /admin/users 时,请求会先到 Nginx。若磁盘上没有同名文件,且 Nginx 没把合法前端路由回退到应用入口 index.html,服务器就会在前端代码启动前返回 404。

查看配置和执行 nginx -t 通常不收费,也不会改数据。真正风险是把所有路径都无条件回退到 index.html:API、上传文件、图片和真实不存在的地址也可能返回 200 HTML,导致接口报解析错误、缓存污染和搜索引擎把无效页当成软 404。默认做法是先确认这确实是纯 SPA 静态站,再只在前端路由所在的 location 使用 try_files,把 /api/、上传目录和静态资源单独分流。

先判断 404 来自 Nginx、前端路由还是 API

不要只看浏览器白页。打开开发者工具“网络”,或从 SSH、宝塔终端对首页、一个有效深层路由、一个不存在路由和一个 API 地址分别发请求。只读测试不会改变站点,但示例域名与路径必须替换成自己的值。

curl -I https://www.example.com/
curl -I https://www.example.com/admin/users
curl -I https://www.example.com/this-route-should-not-exist
curl -i https://www.example.com/api/health

www.example.com、深层路由和健康检查地址换成实际站点。若首页 200、前端点击正常、刷新有效路由 404,通常是服务器回退缺失;若深层路由返回 200 但页面内显示前端 404,应检查前端路由表和基础路径;若 API 返回以 <!doctype html> 开头的 200 响应,说明 SPA 回退错误吞掉了 API 请求。

检查结果更可能的原因下一步
点击路由正常,刷新 Nginx 404History 路由没有回退到入口文件确认站点根目录后配置受限的 try_files
刷新返回 200,但 JS/CSS 404构建 base 与部署子目录不一致核对资源 URL、站点 root 和子目录前缀
API 返回 200 HTML通配 SPA 回退覆盖了 API location把 API 位置单独代理,并确保优先匹配
任意乱写地址都返回首页 200前端缺少兜底 404,或主机只会全量回退增加前端未匹配路由,并评估 SSR/预渲染
仅子目录部署失败路由基路径、构建 base 与 Nginx location 不一致统一三处前缀后重新构建

第一步:确认你部署的是 SPA 还是 SSR/静态预渲染

纯 SPA 通常只有一个入口 HTML,其他页面由浏览器加载 JavaScript 后渲染;它适合把合法前端路由回退到入口。SSR 应由 Node 或其他应用服务器处理路由,静态预渲染则可能为每个地址生成独立 HTML。Nuxt、Next.js 或 React Router Framework 项目不能只凭 dist 目录名判断模式。

先查看构建脚本、框架部署说明和输出目录:

pwd
node --version
node -e "const p=require('./package.json'); console.log(p.scripts)"
find dist -maxdepth 2 -type f -name '*.html' 2>/dev/null | head -n 30
find build -maxdepth 3 -type f -name '*.html' 2>/dev/null | head -n 30

distbuild 只是示例目录。若输出里确实只有一个应用入口,且框架使用浏览器 History 路由,才进入下面的 Nginx 回退配置。SSR 项目应检查上游服务、proxy_pass 与运行进程,不能把所有请求改成静态首页;这类错误可参考Nginx 反向代理与 HTTPS 配置继续分层。

第二步:在宝塔确认站点 root 和生效配置

宝塔面板进入“网站 → 对应站点 → 设置 → 配置文件”。先记录 server_nameroot、现有 location、API 代理和伪静态规则,再从终端检查 Nginx 实际加载的配置。不要只编辑项目源码里的示例文件,因为线上 Nginx 可能加载另一份站点配置。

sudo nginx -T | grep -nE 'server_name|root |location |try_files|proxy_pass'
sudo nginx -t

nginx -T 会输出完整配置,可能包含内网地址、证书路径和站点名称,对外分享前必须脱敏。确认 root 指向本次构建产物,入口文件存在,并且没有另一个更具体的 location 抢先处理深层路由。

第三步:只给前端路由增加 History 回退

对于部署在域名根目录的纯 SPA,Vue Router 官方给出的 Nginx核心方式是让真实文件、真实目录优先,最后回退到 /index.html。下面是结构示例,域名、目录和 API 上游必须按实际环境替换。

server {
    listen 80;
    server_name www.example.com;
    root /www/wwwroot/example/dist;
    index index.html;

    location ^~ /api/ {
        proxy_pass http://127.0.0.1:8080/;
    }

    location ^~ /uploads/ {
        try_files $uri =404;
    }

    location ^~ /assets/ {
        try_files $uri =404;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
}

try_files 会按顺序检查文件与目录,最后一个参数触发内部跳转。API 与上传目录没有命中时应返回真实错误,不能回退到前端首页。若 API 需要保留原始路径,proxy_pass 是否带尾斜杠会改变上游 URI,必须先对照现有后端路由,不要照抄示例。

保存前备份现有站点配置,执行语法检查;只有 nginx -t 成功才平滑重载。保持当前 SSH 会话和宝塔入口,避免错误配置后失去恢复通道。

sudo cp -a /path/to/site.conf /path/to/site.conf.before-spa-fallback
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl is-active nginx

两个 /path/to/site.conf 必须换成刚才从生效配置确认的真实文件。复制和修改配置不会直接改数据库,但错误重载可能中断网站;若语法检查失败,不要执行 reload。

部署在子目录时为什么仍会 404

例如应用访问路径是 /console/,需要同时统一三处:构建工具的资源 base、前端路由的基路径、Nginx 的 location 与回退入口。只把根目录示例改成 /index.html,浏览器可能回到整站首页,而不是子应用入口。

location ^~ /console/assets/ {
    try_files $uri =404;
}

location /console/ {
    try_files $uri $uri/ /console/index.html;
}

重新构建后检查 HTML 里的 JS/CSS 地址是否带正确前缀。若资源返回 HTML,浏览器常显示 MIME 类型错误或白屏,这不是再增加一条 rewrite 能解决的问题。Vite 官方说明构建 base 要与部署路径一致;Vue Router 也要求子目录部署同步调整路由基础路径。

不要让 SPA 回退吞掉真实 404

服务器把未知路径交给 index.html 后,前端路由仍要有未匹配页面。例如 Vue Router 可以配置 catch-all 路由显示“页面不存在”。但纯 SPA 的服务器响应状态通常仍是 200;对于依赖准确 HTTP 状态、需要搜索收录的公开内容站,更适合 SSR、静态预渲染或由边缘/服务器明确输出 404。

后台管理系统的登录、权限和 API 也不能只依赖前端路由隐藏。Nginx 回退只是路由交付,不是访问控制;管理端接口仍需服务端认证、权限检查和安全响应。不要为修刷新 404 把后台目录、Source Map、环境文件或上传目录一起公开。

修复后如何验收

清理浏览器缓存前,先用 curl 分别验证状态和内容类型。有效深层路由应返回入口 HTML;静态资源应返回正确的 JavaScript/CSS 类型;API 不应返回前端 HTML;真实文件缺失和未知 API 应保留 404。再用无痕窗口从地址栏直接打开深层路由并刷新。

curl -sS -o /dev/null -w '%{http_code} %{content_type}\n' https://www.example.com/admin/users
curl -sS -o /dev/null -w '%{http_code} %{content_type}\n' https://www.example.com/assets/app.js
curl -sS -o /dev/null -w '%{http_code} %{content_type}\n' https://www.example.com/api/not-found
curl -sS https://www.example.com/api/health | head

资源文件名、路由和 API 地址必须替换。还要检查登录跳转、前进后退、直接分享链接、强制刷新、错误页和 CDN 缓存。若站点前面有 CDN,先确认源站正常,再按CDN、DNS 与回源排查检查节点是否仍缓存旧 404。

怎样回退

若修改后 API 变成 HTML、资源白屏或未知页面大量返回 200,立即恢复备份的站点配置,执行 nginx -t 后再平滑重载。保留故障时的响应头、命中的 location 和访问日志,重新设计 API、上传目录与前端路由边界。配置恢复后按Nginx 403 与站点目录排查复核 root、权限和首页文件,不要用全局 777 或全局 rewrite 掩盖路径错误。

官方与上游依据

云服务器教程Docker 提示 pull access denied 怎么办?镜像名、仓库权限与登录排查2026-09-13云服务器教程MySQL 报 ERROR 1205 Lock wait timeout 怎么办?阻塞事务与安全止损2026-09-12云服务器教程Cloudflare 报 Error 524 怎么办?源站慢请求、超时与安全恢复2026-09-12

加入开发者交流社区

与全球开发者、运维和工作室一起交流技术、分享经验、配置、账号与最新优惠信息

  • 云平台使用交流
  • 资源优惠信息
  • 最新教程与资讯
  • 开发者经验分享
联系 Telegram 客服
加入开发者交流社区