ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

若依Vue前后端分离Nginx部署404排查实战

若依Vue前后端分离Nginx部署404排查实战 1. 一个下午的部署踩坑记从页面能开到全线404若依这套前后端分离的框架本地npm run dev跑得好好的一打包丢到 Nginx 上就开始各种花式报错——页面刷新白屏404、验证码图片死活加载不出来、/prod-api下的系统资源接口全线404、后端接口也跟着404。我上个项目迁移环境前后端加运维这块我一个人扛从下午两点折腾到晚上八点把这几个坑挨个趟了一遍。这篇东西就是给正在或即将用 Nginx 部署若依 Vue 的同学看的不管你是刚接触若依框架的新手还是踩过一点坑但没搞明白原理的老手看完应该能省下我那天浪费掉的六个小时。先说清楚适用场景若依的前后端分离版本RuoYi-Vue前端用 Vue2 或 Vue3 Vite/Webpack 打包成静态文件由 Nginx 托管后端是 Spring Boot 打成的 jar独立跑在某个端口上。Nginx 在中间既要当静态资源服务器又要当反向代理把/prod-api之类的请求转发给后端。这套结构里 404 的来源一共有四类我按踩坑顺序逐个拆。我先把结论性的东西摆出来你可以对照自己卡在哪一步现象大概率根因定位位置首页正常刷新或直接访问子路由就404history 路由模式缺少try_files回退Nginxlocation /验证码图片、图标等静态资源404打包路径publicPath配置错误vue.config.js/vite.config.js/prod-api/...接口404反向代理路径重写没做对Nginxlocation /prod-api后端接口直接404后端 context-path 与代理前缀不匹配application.yml这四个问题看起来分散其实根子上都跟路径两个字有关浏览器请求的路径、Nginx 匹配的路径、转发给后端的路径、后端实际暴露的路径这四者但凡有一个对不上404 就来了。搞清楚这条链路剩下的就是填空题。注意下面的配置和参数我都基于若依前后端分离版的默认约定来写如果你改过端口、前缀、打包输出目录记得把对应值替换掉不要照抄。1.1 为什么本地跑得好好的一上 Nginx 就崩这是最多人困惑的地方。本地开发时你应该是这样跑的前端npm run dev起一个开发服务器比如在localhost:80vue.config.js里配了devServer.proxy把/dev-api代理到后端的localhost:8080。注意这里有个关键点——开发服务器的代理配置和 Nginx 的代理配置是两套东西互不相干。你本地能通是因为 webpack-dev-server 帮你做了代理打包之后这套代理就不存在了全部依赖 Nginx 重配一遍。很多人打包后只把dist目录丢给 Nginx以为代理配置会跟着走结果就是接口全404。还有一个容易被忽略的点若依前端默认用的是history 模式路由createWebHistory或 Vue Router 的mode: history。history 模式的好处是 URL 干净没有#但代价是刷新和直接访问子路由时浏览器会真的拿这个路径去请求服务器。比如你访问/system/user浏览器就向 Nginx 发一个/system/user的请求Nginx 去dist目录里找system/user这个文件或目录当然找不到404。开发服务器默认帮你做了 fallback找不到就返回index.htmlNginx 不会自动做得你自己配try_files。这就是本地好好的、上线就崩的完整逻辑链。理解这一点后面所有配置你才配得明白而不是靠背。2. 部署前的环境准备与材料清单动手之前先确认你手里的东西齐不齐。我发现很多 404 其实是环境本身就不对比如 Nginx 装错版本、打包命令跑成了开发模式、后端根本没起来排查了半天最后发现是后端没启动这种低级问题。所以这一节先把准备工作做扎实。2.1 需要准备哪些东西按若依前后端分离的标准结构你需要一台 Linux 服务器CentOS 7/8、Ubuntu 20.04/22.04 都行我个人更习惯 Ubuntuapt 装 Nginx 省心或者本地虚拟机、Docker 容器也行原理一样。Nginx版本建议 1.20 以上。老版本对某些指令支持不完整但核心的try_files、proxy_pass早就有了1.18 也够用。装法后面说。JDK看你后端用的什么版本。若依新版一般是 JDK 8 或 17跑起来验证一下java -version。后端打好的 jar 包以及前端打好的dist目录。数据库MySQL、Redis 这些按若依的要求来但这个跟 404 关系不大属于后端能不能起来的范畴。前端打包这一步很多人出错我强调一下命令的区别# 生产环境打包输出到 dist 目录 npm run build:prod # 如果你用的是若依 Vue3 版本脚本名可能是 build npm run build千万不要用npm run dev去打包那个是起开发服务器的不会生成可部署的静态文件。打包完成后打开dist/index.html如果里面引用的 js/css 路径是/static/js/xxx.js这种说明路径正常如果是./static/...或者干脆是绝对路径带着你的本地目录名那部署上去资源必404。这个我后面在静态资源那一节细讲。2.2 Nginx 怎么装以及装之前要拎清的目录Ubuntu 下最简单sudo apt update sudo apt install nginx -y sudo systemctl enable nginx sudo systemctl start nginxCentOS 下要先加源或者用 yumsudo yum install epel-release -y sudo yum install nginx -y sudo systemctl enable nginx sudo systemctl start nginx装完之后你要知道 Nginx 的目录约定这是后面排查问题的地图路径作用/etc/nginx/nginx.conf主配置一般不动/etc/nginx/conf.d/*.conf站点配置我们的配置写这里/usr/share/nginx/html默认静态根目录不推荐把若依放这/var/log/nginx/access.log访问日志排查404看它/var/log/nginx/error.log错误日志看为什么报错我个人的习惯是不往/usr/share/nginx/html里塞项目而是单独建一个目录比如/www/wwwroot/ruoyi-ui把dist里的内容拷进去。这样方便管理多个站点也方便后面挂载磁盘或做备份。conf.d下的配置文件同理一个项目一个.conf不要所有东西都堆进nginx.conf否则后面多个站点一起管会乱成麻。提示改完配置一定要先sudo nginx -t测试语法通过了再sudo nginx -s reload平滑重载。直接 reload 一个写错的配置Nginx 可能起不来线上就整个挂了。这个习惯能救你很多次。2.3 一个最小可用的部署目录结构我会这么组织/www/wwwroot/ruoyi-ui/ # 前端静态文件根目录 ├── index.html ├── static/ └── favicon.ico /www/wwwroot/ruoyi-backend/ # 后端 jar 放这 └── ruoyi-admin.jar然后启动后端cd /www/wwwroot/ruoyi-backend nohup java -jar ruoyi-admin.jar --spring.profiles.activeprod app.log 21 用nohup配合让它在后台跑日志重定向到app.log。生产环境更推荐 systemd 托管但排查阶段先用 nohup 看日志最快。启动后先在本机 curl 一下确认后端活着curl http://127.0.0.1:8080/能返回点东西哪怕是 404 的 JSON就说明后端起来了。这一步非常重要——如果后端本机都访问不通那 Nginx 那层再怎么配都是徒劳。我那天一开始就是在 Nginx 上瞎配后来才发现后端 jar 因为数据库连不上根本没起来。3. 核心404问题逐个拆解与配置实操这一节是全文的重头戏四种404我一一种拆每个都给完整配置和参数解释。你可以对着自己的现象直接跳读。3.1 页面刷新404history 模式的锅用 try_files 收尾现象打开首页一切正常点菜单跳转也正常但只要在/system/user这种子路由上按 F5 刷新或者直接把 URL 发给同事让他打开就400/404白屏。原因前面说了history 模式下刷新会让浏览器真请求/system/user。解决办法是在location /里加try_filesserver { listen 80; server_name your-domain.com; location / { root /www/wwwroot/ruoyi-ui; index index.html; try_files $uri $uri/ /index.html; } }try_files的执行逻辑是先找$uri对应的文件找不到再找$uri/目录都找不到就返回最后的/index.html。把index.html返回给浏览器后Vue Router 自己接管路由把这页渲染出来。这就是为什么刷新子路由能正常了。这里有个新手很容易踩的坑写成try_files $uri $uri/ /index.html 404最后被那个404干掉了等于白配。还有的人root写错指向了一个不存在的目录那try_files第一步就全失败返回index.html也找不到还是404。所以配完记得ls /www/wwwroot/ruoyi-ui/index.html确认文件在。如果你是 Vue3 Vite 的若依版本路由模式可能用的是createWebHistory()原理完全一样try_files照配。唯一区别是 Vite 打包输出配置在vite.config.js的base字段这个跟try_files无关但会影响静态资源路径下一节讲。实操心得try_files的本地测试方法是配好后先curl -I http://127.0.0.1/system/user如果返回200且Content-Type: text/html说明回退成功返回404就是没配好。比开浏览器刷新快多了。3.2 验证码和系统资源404打包 publicPath 配错路径全歪了现象首页能开但验证码图片不显示F12 看到GET /prod-api/captchaImage 404或者图片路径指向一个不存在的静态目录另外/static、/favicon.ico这类静态资源也404。这个问题要分两种可能来看。第一种是静态资源路径配置错。若依前端打包时publicPathVue2 Webpack 里叫这个Vue3 Vite 里叫base决定静态资源引用时加什么前缀。默认若依一般配的是/也就是从根路径开始引用。如果你手贱改成了./或者某个子目录名那打包出来所有资源的引用都会带上这个前缀部署到 Nginx 根路径下就全部404。检查办法打开dist/index.html看里面的script src...。正确的是src/static/js/app.xxx.js如果变成src./static/js/app.xxx.js或者src/your-subdir/static/...就是配错了。// vue.config.js module.exports { publicPath: process.env.NODE_ENV production ? / : /, outputDir: dist, }正确做法就是开发和生产都用/除非你确实要把前端部署到子路径下那才去改同时 Nginxlocation也要跟着改。我见过有人把生产环境的publicPath改成了/ruoyi-ui/想着部署在子目录更清晰结果所有资源都去/ruoyi-ui/static/...找而 Nginx 根本没这个路径全404。要么就别改要么 Nginx location 和 publicPath 保持一致这是铁律。第二种可能是验证码接口本身404。验证码不是静态资源它是请求后端/captchaImage接口拿到的。如果 F12 网络面板看到这个请求是404那问题就不在静态资源而在反向代理路径。这种情况下一节细说。判断方法很简单看404请求的 URL 是xxx.js还是captchaImage前者是静态资源问题后者是接口代理问题。先分清是哪一类再动手别瞎配。3.3 后端接口404反向代理路径重写最容易出岔子的地方现象页面正常静态资源正常但所有/prod-api/xxx请求全部404。这是若依部署最高频的坑。若依前端的接口请求有个基础路径叫VUE_APP_BASE_APIVue2或import.meta.env.VITE_APP_BASE_APIVue3生产环境一般是/prod-api。这个前缀的用意是前端只请求/prod-api/xxx由 Nginx 识别这个前缀去掉它转发给后端的/xxx。所以 Nginx 的代理配置要完成两件事匹配/prod-api前缀以及把前缀去掉再转发。正确写法location /prod-api/ { proxy_set_header Host $http_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; proxy_pass http://127.0.0.1:8080/; }关键在那个proxy_pass结尾的斜杠。我给你把规则讲透proxy_pass http://127.0.0.1:8080/;带斜杠Nginx 会把location匹配到的部分这里是/prod-api/替换成/也就是/prod-api/system/user→http://127.0.0.1:8080/system/user。这是我们想要的。proxy_pass http://127.0.0.1:8080;不带斜杠Nginx 保留完整路径/prod-api/system/user→http://127.0.0.1:8080/prod-api/system/user。后端没有/prod-api这个前缀直接404。我那天卡了快一个小时就是漏了那个尾斜杠。看起来就一个字符的区别结果截然不同。很多教程也不讲这个区别只说加个斜杠就好了你不理解原理下次换个场景又懵。还有一个细节location /prod-api/上的location也建议带斜杠。如果写成location /prod-api不带斜杠虽然大多数情况也能匹配但遇到/prod-api这种恰好没后续路径的请求时行为会有微妙差异。统一带斜杠能省掉一些边界问题。注意如果你的后端配了context-path比如server.servlet.context-path: /ruoyi那后端实际暴露的路径是/ruoyi/xxx上面那条proxy_pass转发过去就会404。解决办法是把代理路径改成proxy_pass http://127.0.0.1:8080/ruoyi/;或者干脆改后端把 context-path 去掉。前端前缀、Nginx 匹配、重写结果、后端 context-path这四者必须闭环任何一个对不上就是404。3.4 静态资源的 MIME 与缓存404 之外的另一类暗坑除了404很多人还会遇到资源返回了但浏览器不执行的情况比如控制台提示Refused to apply style because its MIME type (text/html) is not a supported stylesheet MIME type。这个现象的技术背景是如果浏览器请求了一个 CSS/JS 文件服务器实际返回的是index.html因为try_files把它兜底了那浏览器拿到的是 HTML 内容但期望是 CSS于是拒绝执行。表现为页面样式全丢或者脚本不跑看起来像资源404其实根源是try_files把不存在的资源也回退到index.html了。这个坑的诱因通常是静态资源路径配错就是 3.2 那种浏览器去请求一个不存在的路径try_files兜底返回 HTML于是内容类型不匹配。所以治本还是把路径配对。但如果你的站点里有独立的静态资源目录建议单独配location并设置缓存location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { root /www/wwwroot/ruoyi-ui; expires 7d; add_header Cache-Control public, max-age604800; access_log off; }这样静态资源匹配到独立规则不会走try_files兜底既能正确返回内容类型又能加缓存减少请求。注意root要写对写错的话这个 location 里匹配到的资源也404。3.5 Nginx 日志定位404时最该先看的地方与其猜不如看日志。Nginx 的access.log会记下每一个请求的路径和返回码。排查404的正确姿势# 实时看访问日志过滤出404 sudo tail -f /var/log/nginx/access.log | grep 404 然后你在浏览器里操作触发404日志里就会实时滚出来。看那个请求的完整路径你立刻就能判断如果是/system/user这种前端路由 → history 回退没配。如果是/static/js/xxx.js找不到 → 静态路径问题。如果是/prod-api/xxx→ 代理重写问题。如果是/some-random-path→ 前端请求路径本身错了。error.log则记录 Nginx 层面的错误比如open() /www/wwwroot/ruoyi-ui/xxx failed (2: No such file or directory)直接把缺哪个文件告诉你。这两个日志配合看定位速度能提升好几倍。我那天后来养成习惯配完一段就先 tail 日志再刷新基本一分钟定位。4. 完整可复刻的 Nginx 配置与部署流程前面拆得比较散这一节我把一份能直接用的完整配置和部署步骤串起来你可以整体拿走改改就用。4.1 一份覆盖四种404的完整配置server { listen 80; server_name your-domain.com; # 字符集避免中文乱码 charset utf-8; # 前端静态资源根目录 root /www/wwwroot/ruoyi-ui; index index.html; # 前端页面history 模式回退 location / { try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /prod-api/ { proxy_set_header Host $http_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; proxy_connect_timeout 30s; proxy_read_timeout 60s; proxy_send_timeout 30s; proxy_pass http://127.0.0.1:8080/; } # 静态资源独立处理加缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?|ttf|eot)$ { expires 7d; add_header Cache-Control public, max-age604800; access_log off; } # 大文件上传限制若依有文件上传功能 client_max_body_size 50m; # 访问和错误日志 access_log /var/log/nginx/ruoyi.access.log; error_log /var/log/nginx/ruoyi.error.log; }这份配置基于常见实践整理已经涵盖了 history 回退、接口代理、静态资源、上传限制这几个关键点。proxy_connect_timeout这类超时参数我给了保守值实际按你网络情况调本地回环一般很快30秒绰绰有余。4.2 从打包到上线的完整步骤我把整个流程按顺序列一遍每一步都带验证点你对着做能少走弯路打包前端在若依前端目录执行npm run build:prodVue3 版本可能是npm run build确认生成dist目录且里面有index.html。检查打包产物路径打开dist/index.html确认资源引用是/static/...形式而不是./static/...。这一步能提前避免静态资源404。上传文件把dist里的内容拷到服务器/www/wwwroot/ruoyi-ui/。启动后端nohup java -jar ruoyi-admin.jar --spring.profiles.activeprod app.log 21 然后curl http://127.0.0.1:8080/验证后端活着。写 Nginx 配置把 4.1 那份配置改成你的域名和路径放到/etc/nginx/conf.d/ruoyi.conf。测试并重载sudo nginx -t通过后sudo nginx -s reload。验证首页浏览器打开域名首页应正常显示。验证子路由刷新直接访问一个子路由并刷新比如/system/user应该不404。验证接口看网络面板/prod-api/xxx请求返回200。看日志兜底如果哪一步不对tail -f两个日志定位。这十步走完正常的若依部署基本就通了。我给个参数上的说明proxy_read_timeout 60s这个值针对的是后端处理慢查询的场景若依有些报表导出、大数据量列表查询可能超过默认的60秒如果你遇到接口超时而不是404可以往大调。但这属于另一个问题域404排查用不到。4.3 多种部署形态的差异点如果你不是在单机裸机上部署而是用 Docker、Docker Compose 或者 Kubernetes配置思路一样但有几个差异要注意Docker 部署前端通常用官方nginx镜像把dist和你的.conf挂载进去或者用多阶段构建打进镜像。关键是proxy_pass里的地址不能写127.0.0.1:8080因为容器里127.0.0.1指容器自己后端在另一个容器。得用容器名或 Docker 网络里的服务名比如proxy_pass http://ruoyi-backend:8080/;。K8s 部署后端用 Service 暴露proxy_pass指向 Service 名比如http://ruoyi-server.default.svc.cluster.local:8080/。若依微服务版在 K8s 上还要考虑网关的路径转发比单体复杂但 404 排查逻辑不变还是看请求路径怎么被改写。Docker Compose 一起编排前后端放同一个 networkNginx 容器里proxy_pass用服务名即可注意depends_on保证后端先起来不然 Nginx 起来时后端还没就绪会短暂502不是404但也挺烦。这些形态下日志查看方式也变了Docker 用docker logsK8s 用kubectl logs但看的内容还是请求路径和返回码那一套。5. 排查实战常见404速查与避坑心得这一节我把实际踩过的坑和排查思路整理成速查表外加几条文档里不会写的经验。5.1 404 问题速查表404 的 URL 长相根因解决办法/system/user等前端路由history 模式无回退location /加try_files $uri $uri/ /index.html/prod-api/captchaImage代理重写路径不对proxy_pass结尾加斜杠去前缀/static/js/app.xxx.jspublicPath/base配错改为/重新打包/prod-api/prod-api/xxx重复前缀检查proxy_pass是否误保留了匹配段后端日志报找不到路径context-path 不匹配代理路径补上或去掉 context-path/favicon.ico404图标文件没上传确认dist里的 favicon 一起拷过去API 返回 HTML 非 JSONtry_files兜底拦了接口把接口 location 放在页面 location 之前或路径不重叠最后一行那个坑我单独说下。Nginx 的 location 匹配有优先级如果你把location /写在location /prod-api/前面一般情况下因为/prod-api/更具体仍然会优先匹配但如果你用的是正则匹配又没注意顺序可能出现接口请求被前端 location 拦截、返回index.html的情况。表现就是前端拿到一个 HTML 字符串当 JSON 解析报 Unexpected token 而不是明显的404。这个坑比404更隐蔽值得警惕。保险做法是把接口的 location 写在前面或者用精确/前缀匹配明确区分。实操心得遇到接口返回的不是 JSON这类诡异问题先看响应体的前几个字符是不是!DOCTYPE是的话基本就是被try_files或默认 location 兜底了顺着 location 顺序查。5.2 我踩过的三个真实坑第一个是尾斜杠。前面说得够细了我那天从proxy_pass http://127.0.0.1:8080;改成带斜杠的瞬间接口全通。教训就是proxy_pass的斜杠不是可有可无的装饰是有语义的。建议你直接把带斜杠的版本当成标准写法除非明确知道自己在做什么别的情况。第二个是打包用了 dev 模式。我一开始图省事跑了个npm run dev然后以为它会在某处生成静态文件折腾了半天找不到dist还以为是构建工具出问题。后来才反应过来 dev 是开发服务器得有npm run build才产出。这个坑幼稚但真实发生了写出来提醒后来人。打包命令和开发命令永远别混。第三个是缓存捣鬼。有一次配好了所有东西接口也通了但页面还是老样子报错。F12 看到部分资源加载的还是旧的浏览器一直从缓存拿。解决办法是Ctrl Shift R强刷或者开发阶段在 Nginx 里给 HTML 关掉缓存location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; }入口 HTML 不缓存里面的资源引用带哈希有变更时浏览器会重新拉。生产环境上线时用这一招能避免用户拿到旧版本页面报错的假故障。5.3 几条不写在文档里的经验部署这类事我总结了几条心法比具体命令更值钱一次只改一个变量。别同时改 Nginx 配置、打包参数和后端配置那样出问题你根本不知道是谁引起的。我那天中间就犯过这个错三个地方一起动最后全乱套只能回滚重来。日志优先于猜测。tail -f access.log | grep 404这个命令我这几年用了几百次比任何我猜可能是XX的排查都快。后端本机先自测。curl 127.0.0.1:8080通了再管 Nginx这一步能排除掉一半以上的伪 Nginx 问题。很多所谓 Nginx 404其实是后端压根没起来。配置备份。改 Nginx 配置前先cp一份改坏了立刻回滚。生产环境上尤其重要一个字符错误可能导致整站不可用。域名和端口分清。80 端口被占、防火墙没开、安全组没配这些也会表现为访问不了但通常是连接超时或拒绝不是404。404 是连上了但找不到资源和连不上要区分开排查方向完全不同。我还想强调 context-path 那个坑它比其他几个更隐蔽。若依单体默认 context-path 一般是空的但如果你是从某个版本或者自己改过可能带上了。前端前缀、Nginx 匹配、重写结果、后端 context-path 这四者要闭环任何一个不一致就是404而且报错的 URL 会呈现出重复前缀或缺失前缀的特征对照速查表能快速对上。5.4 后续可以扩展的方向这套部署跑起来之后还有不少可以优化的地方。比如加上 HTTPS用 Lets Encrypt 免费证书配好 443 和自动续期或者前面挂一层负载均衡后端起多个实例做横向扩展再或者把静态资源和接口分离到不同域名利用浏览器并发和 CDN 加速。这些都建立在你把基础404问题解决干净的前提下。另外若依微服务版RuoYi-Cloud在 Nginx 层的配置和单体不太一样请求要先经过网关再由网关路由到各微服务。这时候proxy_pass的目标是网关地址网关内部再做路径转发404 的排查思路还是一样——看请求路径在哪一层被改写错了。微服务场景下路径前缀多、网关路由规则复杂更容易出现前缀对不上的问题建议每一层的路径都打印日志确认。我自己的做法是把这份基础配置存成一个模板新项目部署时直接复制改改域名、路径和端口就能用。几次下来就会发现若依部署的404无非就是那几个路径问题摸清链路的每一环再遇到新花样也能从容定位。踩坑不可怕把坑记下来变成自己的排查手册下次就是别人踩坑你三分钟解决。
返回列表