Vue/React SPA 打包部署后,子路由刷新 401 未登录问题彻底解决

今日遇到一个奇怪的问题,页面刷新就报登录失效,我刚登录也是报这个问题,经过仔细查找,发现SPA 单页项目部署 Nginx 后刷新子路由报错,算是老生常谈的经典坑了。但这次遇到的情况和常规 404 不一样,是子路由刷新直接报 401 未登录,坑点藏得更深,很多新手甚至中级前端都会被绕进去。

今天完整复盘一下这次线上真实问题,从现象、根因、踩坑点、最终落地方案一次性讲透,彻底根治 SPA 部署路由异常问题。

一、问题现象(线上真实场景)

项目技术栈:React + Vite + Java SpringBoot 后端,Nginx 反向代理部署。

线上环境出现诡异问题:

  • 网站首页 https://admin\.xxx\.com/ 访问、登录、所有功能完全正常

  • 登录后通过前端菜单跳转页面(如 /admin/model)一切正常

  • 只要手动刷新子路由页面,直接弹出 401 未登录/登录过期

返回报文就是后端标准拦截提示:{"code":401,"message":"未登录或登录已过期"}

最让人迷惑的一点:本地 Vite 开发环境 完全没有任何问题,只有打包上线后才复现。

二、初期误区(绝大多数人都会踩的坑)

刚开始排查的时候,我第一反应也是:是不是 token 刷新失效、cookie 丢失、登录态没存住?

反复检查了一遍前端逻辑:

  • localStorage 存储 token 正常

  • 请求拦截器统一携带 Authorization 请求头正常

  • 本地代理调试所有接口鉴权正常

前端逻辑毫无问题。

接着又怀疑后端拦截器规则问题,但后端接口本身鉴权是稳定的,正常页面跳转、接口请求全部不会 401,唯独刷新路由报错。

三、真正根因(核心本质)

这里必须搞懂一个绝大多数前端模糊的知识点:Vite 本地代理 和 线上 Nginx 代理完全是两套逻辑。

本地开发时:

浏览器访问 localhost:5175/admin/model,是 Vite 服务接管路由,前端路由匹配页面,接口走 Vite 代理转发到 Java 8082 端口。

页面路由不会经过后端,只有接口会经过后端,所以永远不会 401。

线上部署时,出问题的根源就在最初的 Nginx 配置:

最初配置是全量代理,所有请求(不管是页面路由还是接口)全部转发给 Java 后端:

浏览器刷新https://admin.xxx.com/admin/model ➜ Nginx 直接转发到 Java 服务

Java 后端的逻辑是:所有非接口路径,没有对应接口,统一走全局拦截器鉴权。

后端把 /admin/model 当成了一个后端接口请求,而不是前端页面路由,没有登录态,直接拦截返回 401。

一句话总结根因:

SPA 前端路由刷新,被 Nginx 错误转发给 Java 后端,后端鉴权拦截器误拦截页面 GET 请求,导致刷新子路由 401。

四、理清架构:页面路由 和 接口路由 必须拆分

我们这套项目架构非常典型:

  • 前端:纯静态 SPA 资源(打包后 index.html + js/css)

  • 后端:Java SpringBoot 只负责提供接口,不负责页面渲染

  • 接口分为两类:/api/xxx 业务接口、/auth/xxx 登录鉴权接口

正确的线上转发逻辑必须是:

  • 所有页面路由(/、/admin、/admin/model...):交给 Nginx,返回 index.html,由前端 JS 路由解析

  • 所有接口路由(/api、/auth):反向代理转发给 Java 8082 后端

之前的致命错误就是:页面路由也被当成接口转发给后端了。

五、最终落地解决方案(生产可用、稳定根治)

1、Nginx 核心配置修正(关键)

拆分路由规则,页面走静态兜底,接口走后端代理,彻底解决刷新 401/404 问题:

单独代理后端两类接口,其余全部交给前端 SPA 路由:

nginx 复制代码
# 业务接口代理
location /api/ {
    proxy_pass http://127.0.0.1:8082;
    proxy_http_version 1.1;
    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;
}

# 登录鉴权接口代理(对应Vite本地/auth代理)
location /auth/ {
    proxy_pass http://127.0.0.1:8082;
    proxy_http_version 1.1;
    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;
}

# 所有页面路由兜底,解决SPA刷新问题
location / {
    try_files $uri $uri/ /index.html;
}

2、本地 Vite 配置保留(无需改动)

本地开发代理逻辑正常,保留双代理配置,兼顾业务接口和登录鉴权接口:

typescript 复制代码
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  server: {
    port: 5175,
    host: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8082',
        changeOrigin: true,
        rewrite: (p) => p,
      },
      '/auth': {
        target: 'http://localhost:8082',
        changeOrigin: true,
      },
    },
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
    },
  },
});

六、改完之后的正确逻辑

1、刷新 /admin/model

Nginx 找不到对应静态文件 → 兜底返回 index.html → 前端 JS 加载后自行匹配路由 → 不会请求后端,无 401

2、页面发起登录、业务请求

/auth/login、/api/xxx 匹配 Nginx 代理规则 → 正常转发 Java 后端 → 正常鉴权、正常返回数据

七、开发多年总结的 SPA 部署核心经验

1、永远不要让 Nginx 把前端路由转发给后端接口服务,这是 90% SPA 部署诡异报错的根源。

2、Vite、Webpack 的本地代理只在开发环境生效,打包后完全失效,千万不要用本地开发逻辑去臆测线上环境。

3、常规刷新 404 是路由找不到,刷新 401 一定是页面路由被后端拦截器拦截,优先排查 Nginx 转发规则,而非前端 token。

4、前后端分离架构,必须严格区分:Nginx 管页面,Java 管接口,职责边界一旦混乱,线上必出诡异问题。

八、最终效果

修改 Nginx 配置并重载后:

  • 所有子路由页面刷新正常,不再 401

  • 登录、登出、token 刷新、所有业务接口完全正常

  • 首页、嵌套路由、多级路由全部稳定运行

问题彻底根治,线上稳定无复发。

相关推荐
Moment2 分钟前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan14 分钟前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么17 分钟前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技43 分钟前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥1 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易1 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭2 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
严肃小白兔3 小时前
TanStack Router 更新构建工具链:Node.js 兼容性、路由分割与前端工程实践
前端·node.js·状态模式
IT_陈寒3 小时前
Vue这个奇葩的响应式问题让我加班到凌晨
前端·人工智能·后端
风骏时光牛马3 小时前
功能可用性自动化测试验证
前端