ARTICLE DETAIL

资讯详情

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

若依前后端分离部署 Nginx 404 排查与反向代理配置

若依前后端分离部署 Nginx 404 排查与反向代理配置 上周接了个活帮朋友把他们内部一套基于若依前后端分离框架的项目从开发机挪到测试服务器上。前端npm run build:prod打完包dist 目录往 Nginx 一扔配置文件改完nginx -s reload浏览器打开一看登录页出来了Logo、背景图、输入框都挺正常心里刚松了一口气随手按了下 F5页面直接变成 Nginx 那个白底黑字的 404。那一刻我就知道今天下午别想干别的了。事实也确实如此。接下来三个多小时我把「页面刷新404」「验证码找不到」「系统资源404」「后端接口404」这几个坑挨个踩了一遍有的是老问题换个马甲又出现有的是我自己的配置写顺手了没改。这篇就把整个排查链路、每一步的为什么、以及最后跑通的配置全部摊开讲清楚适合正在把若依Vue2 或 Vue3 版往服务器上搬、前端用 Nginx 托管、后端单独跑一个 Java 进程的同学参考。哪怕你之前没碰过 Nginx看完也能自己把 404 一个个揪出来。1. 先把若依前后端分离部署的请求链路捋直很多人排查 404 效率低根本原因不是不懂 Nginx而是脑子里没有一张完整的请求地图。请求从浏览器发出去到拿到数据中间到底过了几个环节、每个环节负责什么一旦模糊就只能靠猜。我这次之所以花了三个多小时前半段就浪费在这个上面。1.1 从打开页面到数据渲染请求走了哪几跳若依前后端分离版的部署形态本质上是两套东西一个纯静态的前端工程打完包就是一堆 html/css/js一个跑在 Java 容器里的后端服务默认 8080 端口。浏览器本身不认识后端它只认识 Nginx。所以完整的链路是这样浏览器请求http://你的域名/Nginx 从本地磁盘目录里读出index.html返回浏览器解析index.html发现里面引用了static/js/app.xxxx.js、static/css/chunk-xxxx.css于是继续向 Nginx 要这些静态文件Nginx 还是从磁盘读JS 跑起来之后前端代码向/prod-api/xxx发请求这个请求还是打到 NginxNginx 发现路径以/prod-api/开头匹配到代理规则把请求转发给127.0.0.1:8080后端处理完返回 JSONNginx 原路回给浏览器。看清楚了吗Nginx 在这套架构里扮演了两个完全不同的角色对/它是文件服务器对/prod-api/它是反向代理。这两个角色的配置写在同一份nginx.conf里谁出问题都会表现成 404但排查方式完全不一样。提示判断一个 404 是静态文件没找到还是后端接口没找到最快的办法是看浏览器 Network 面板里那条 404 请求的 Response Headers。里面有Server: nginx且响应体是 Nginx 默认的 404 页面说明请求根本没转发出去如果响应体是后端返回的 JSON比如{code:404,msg:...}说明转发成功了是后端自己说的找不到。这个判断方法看着简单但我见过太多人一上来就去翻后端日志翻了半天发现后端压根没收到请求。1.2 /prod-api 这个前缀的来历它是所有404的共同源头/prod-api这四个字不是 Nginx 规定的也不是后端规定的它是若依前端工程自己约定的一个假前缀。在若依的.env.production文件里Vue3 版则是.env.production里的VITE_APP_BASE_API有这么一行# Vue2 版 VUE_APP_BASE_API /prod-api前端代码里所有 axios 请求的 baseURL 都取这个值所以最终发出的请求是/prod-api/login、/prod-api/captchaImage。而真实的后端接口是不带这个前缀的后端只有/login、/captchaImage。这个前缀存在的唯一目的就是让 Nginx 有一个明确的特征字符串可以匹配从而把接口请求和静态文件请求区分开。它就像快递面单上的转运中心标记本身不是目的地只是告诉分拣员该往哪个方向扔。理解这一点之后很多问题就自解释了Nginx 里 location 写成/api/前端却发/prod-api/那请求直接掉进/的静态文件规则里Nginx 去磁盘找prod-api/captchaImage这个文件当然找不到404proxy_pass结尾带了斜杠但没有做前缀截断转发给后端的是/prod-api/captchaImage后端路由表里没有这一条还是 404前端打包时环境变量没生效实际发出去的是/dev-api/xxx开发环境的前缀Nginx 只配了/prod-api/同样是 404。我在这次排查里遇到的三个 404追到根上全都和这个前缀有关。1.3 部署前必须锁死的三份配置我后来复盘如果在动手之前先花五分钟把这三份配置逐字对一遍能省掉至少一个半小时。配置位置关键项常见错误前端.env.productionVUE_APP_BASE_API/VITE_APP_BASE_API值带了结尾斜杠或写成了/api前端打包产物publicPathVue2/baseVue3设成/但 Nginx root 指向了子目录Nginxserver块location前缀、root、proxy_pass结尾斜杠前缀不匹配、斜杠缺失或多余对这三份配置我的建议是养成一个习惯先确定前端发出去的前缀再让 Nginx 去匹配这个前缀最后确定转发给后端的路径。顺序反了就会一直在改 Nginx 却怎么都不对。另外提醒一句若依 Vue3 版用的是 Vite 而不是 webpack环境变量前缀必须是VITE_代码里读取方式是import.meta.env.VITE_APP_BASE_API。如果你从 Vue2 项目直接抄配置写成VUE_APP_是不会被 Vite 识别的打包出来就是 undefined请求路径会变成undefined/login——这个报错看起来像后端 404实际上是前端环境变量没注入。2. 页面刷新就404history模式与try_files的那点事这是当天遇到的第一个坑也是最经典的一个点菜单、跳路由全都正常只要在某个非根路径的页面上按 F5或者把 URL 复制给别人打开立刻 404。2.1 为什么点菜单好好的一按F5就挂关键在于若依前端路由用的是history 模式而不是 hash 模式。你在浏览器地址栏看到的是http://域名/system/user而不是http://域名/#/system/user。history 模式下路由跳转是前端 JS 用history.pushState完成的浏览器根本不会向服务器发请求页面自然正常切换。但 F5 刷新不一样浏览器会老老实实向服务器请求/system/user这个路径。这时候 Nginx 收到请求走的是location /它把/system/user当成一个文件路径去磁盘上找你的前端目录/system/user这个文件。目录下只有一个index.html和static/当然找不到于是 404。说白了不是 Nginx 配错了而是它根本不知道/system/user应该交给index.html去处理。前端路由的事情得靠一条兜底规则转交给前端。2.2 try_files 的执行顺序以及为什么兜底要写 /index.html修正方式就一行配置location / { root /home/ruoyi/projects/ruoyi-ui; index index.html index.htm; try_files $uri $uri/ /index.html; }try_files的意思按顺序试先找$uri这个文件找不到就找$uri/这个目录还找不到就内部重写到/index.html。这里有两个细节容易被忽略。第一$uri/这一项不能省否则访问某个子目录路径时可能行为不一致第二最后的兜底目标必须是/index.html带斜杠开头写成index.html在某些 Nginx 版本上会因为相对路径解析的问题出现奇怪的结果。我一般直接写成/index.html图个稳。还有一点值得说清楚try_files的兜底是内部重写浏览器地址栏不变。这就是为什么刷新/system/user之后URL 还是/system/user但页面正常渲染出了用户管理界面——因为返回的其实是index.html前端 JS 起来之后自己根据地址栏的路径又走到了对应的路由。注意不要用error_page 404 /index.html;来替代try_files。这个写法确实能让刷新不报错但它会把所有404 都吞掉包括真正的静态资源缺失。等你哪天发现某个 JS 文件没打包进去浏览器却毫无反应排查起来就痛苦了。2.3 root 和 alias 混用导致的首页能开、刷新就死另一种让人迷惑的情形是首页明明能打开说明index.html确实被读到了但一刷新就 404。这时候八成是root和alias写混了。这两个指令的区别用一个例子说清楚。假设前端文件放在/data/web/ruoyi-ui/里面有index.html# 写法Aroot location / { root /data/web/ruoyi-ui; } # 请求 /index.html → 实际读取 /data/web/ruoyi-ui/index.html ✅ # 写法Balias location / { alias /data/web/ruoyi-ui/; } # 请求 /index.html → 实际读取 /data/web/ruoyi-ui/index.html ✅ # 但如果 alias 末尾漏了斜杠alias /data/web/ruoyi-ui; # 请求 /index.html → 实际读取 /data/web/ruoyi-uiindex.html ❌root是拼接alias是替换 location 前缀而且 alias 对结尾斜杠极其敏感。我的建议是部署单个前端项目时优先用 root只有把项目挂在子路径比如/admin/下时才用 alias。混着用是这类有时候好、有时候坏问题的头号来源。2.4 同域名挂多个前端项目时 location 的匹配优先级如果你的服务器上不止一个前端项目那就还要多考虑一层。Nginx location 的匹配优先级大致是精确匹配命中就停^~前缀匹配命中后不再尝试正则~和~*正则匹配按配置文件里的书写顺序普通字符串前缀匹配取最长的那条。举个实际会踩的例子你同时部署了/prod-api/的代理规则和/prod/的前端项目规则那么请求/prod-api/login会同时满足这两个前缀最终命中的是更长的那条/prod-api/这没问题。但如果前端项目的 location 写成了正则location ~ ^/prod它就会在正则阶段抢在普通前缀之前命中把接口请求拐进静态文件目录又是一片 404。所以多项目场景下我的经验是接口代理统一用^~ /prod-api/这种前缀写法别用正则避免和其他项目抢。3. 静态资源整片404publicPath/base 与目录层级没对齐第一个坑解决后我以为万事大吉了结果登录页打开是一片惨白F12 一开Network 面板红了一片。这里说的系统资源 404要分两种理解我在这次都遇到了所以分开讲一种是前端静态资源css/js/字体/图标加载不到另一种是形如/prod-api/system/user/list这种带system前缀的业务接口 404。3.1 先看 Network 里那些 404 的请求前缀长什么样排查静态资源 404第一步永远是打开 Network按 JS/CSS 过滤看几个 404 请求的完整 URL。我当天看到的是http://域名/prod-api/static/js/app.3f8a.js 404。注意这个前缀——它带上了/prod-api。这说明前端打包时把 base 路径设成了/prod-api/或者更常见的情况前端工程被塞进了某个子目录而打包配置没跟着改。正常的静态资源请求应该是http://域名/static/js/app.3f8a.js。前缀里一旦混进了接口用的/prod-api或者版本号之类的多余路径问题一定出在前端的打包配置而不是 Nginx。这时候你去改 Nginx 是白费功夫。还有一种情况是请求路径没问题但目录层级不对比如实际请求/static/js/app.jsNginx 却去/data/web/ruoyi-ui/dist/static/js/app.js找——多了一层dist。这就是下一节要说的。3.2 Vue2 的 publicPath 和 Vue3 的 base配置错了会怎样若依 Vue2 版在vue.config.js里控制资源路径关键项是publicPath旧版本叫baseUrl。若依模板默认大概是这样的module.exports { publicPath: process.env.NODE_ENV production ? ./ : /, outputDir: dist, assetsDir: static, // ... }这里./是相对路径打包后index.html里引用的是static/js/app.js前面没有斜杠。这种情况下只要index.html和static目录是同级放在一起的无论你部署在根路径还是子路径下浏览器都能正确解析。但如果你把publicPath改成/打包产物里就变成绝对路径/static/js/app.js。此时如果你把项目挂在http://域名/admin/下浏览器会去请求http://域名/static/js/app.js而 Nginx 的/又指向另一个项目就会 404。Vue3 版的若依用的是 Vite对应的配置项叫base写在vite.config.js里export default defineConfig({ base: ./, build: { outDir: dist, assetsDir: static } })逻辑是一样的。我给的建议是部署在域名根路径http://域名/下时用/或./都行部署在子路径下时务必用./或者干脆显式写成/admin/并且保证 Nginx 的 location 和它一致。千万不要出现前端按子路径打包、Nginx 按根路径配置这种错配那必然全线 404。3.3 dist 放在哪一层Nginx root 指向哪一层这是我当天真正栽的一个跟头说出来有点丢人。我把打包出来的dist目录整个上传到了/data/web/ruoyi-ui/结果 Nginx 配置写的是location / { root /data/web/ruoyi-ui; }于是实际读取路径是/data/web/ruoyi-ui/static/js/app.js但文件实际在/data/web/ruoyi-ui/dist/static/js/app.js一层之差全线 404。正确的做法只有两种二选一别混方案一把 dist 里面的内容直接铺到部署目录# 假设部署目录是 /data/web/ruoyi-ui unzip dist.zip -d /tmp/dist cp -r /tmp/dist/* /data/web/ruoyi-ui/然后root /data/web/ruoyi-ui;。方案二保留 dist 这层目录root 直接指向它location / { root /data/web/ruoyi-ui/dist; index index.html; try_files $uri $uri/ /index.html; }我个人更推荐方案二因为目录结构清晰重新部署时直接把 dist 整个替换掉就行干净利落。如果你用 Jenkins 或 CI 自动发布方案二也更省事。方案一的好处是可以在部署目录里额外放一些 Nginx 自定义的错误页、favicon 之类的文件。顺便提一个容易忘的点Nginx 的运行用户通常是 nginx 或 www-data必须对这个目录有读取权限。权限不足时会返回 403 而不是 404但如果你的 location 里配置了try_files兜底到/index.html而index.html恰好也读不了那就可能表现为一个很奇怪的 404容易误导方向。上传后顺手chmod -R 755一下成本很低。3.4 上传目录 /profile/ 也常被漏配还有一个经常在部署后第二天才被发现的问题——用户头像、附件图片全部 404。原因是若依后端默认把上传文件放在/home/ruoyi/uploadPath可在application.yml里改而前端访问这些文件的路径是/profile/upload/...。这个前缀同样需要 Nginx 做一次映射location /profile/ { alias /home/ruoyi/uploadPath/; }注意这里用的是alias而不是root因为/profile/这个前缀是要被替换掉的。请求/profile/upload/2024/05/xxx.png会被映射到/home/ruoyi/uploadPath/upload/2024/05/xxx.png。如果用root会变成/home/ruoyi/uploadPath/profile/upload/...多了一层profile又是 404。4. 验证码加载不出来从接口代理一路查到Redis登录页出来后最扎眼的不是样式而是验证码那块是个红色的裂图图标或者干脆一片空白。这就是题目里说的验证码找不到。它的排查路径特别典型值得单独拿出来讲一遍完整链路。4.1 验证码请求的完整链路卡点通常只有三个验证码的请求是GET /prod-api/captchaImage。它要经过浏览器 → Nginx 的/prod-api/locationNginx 反向代理 → 后端 8080 的/captchaImage后端生成验证码图片base64和一个 uuid把 uuid 和答案存进 Redis返回 JSON前端把返回的 base64 塞进img :srccodeUrl显示。对应地卡点也就三个请求没转发出去Nginx 配置问题、后端的 captchaEnabled 开关问题、Redis 不可用。按这个顺序查基本十分钟内能定位。4.2 先看 Network404 还是 500指向完全不同的方向打开 F12Network 面板过滤captchaImage看状态码如果是404 且响应体是 Nginx 的页面说明请求没被代理出去。往上翻请求 URL确认是不是/prod-api/captchaImage再检查 Nginx 的 location 前缀和proxy_pass结尾斜杠如果是404 且响应体是后端 JSON说明转发成功了是后端没有这个路由。常见原因是proxy_pass没做前缀截断后端收到的是/prod-api/captchaImage如果是500那就不是 404 的范畴了通常是后端抛异常往下看日志。我当天遇到的是第一种Nginx 把location /prod-api写成了location /api改过来就好了。4.3 captchaEnabled 与后端配置开关如果请求返回 200但img字段是空字符串那就要往后端的验证码开关上看。若依的CaptchaController会读取配置项captcha.enabled在application.yml里不同版本可能是sys.account.captchaEnabled或数据库参数配置如果为false接口会返回captchaEnabled: false且不生成图片。前端拿到这个值之后就不会渲染验证码输入框。这本身不是 bug但是如果你在开发环境把验证码关掉了测试环境却期待看到验证码就会出现明明没报错但验证码就是不出来。我建议部署完成后直接 curl 一下这个接口看返回体curl -i http://127.0.0.1:8080/captchaImage返回里如果captchaEnabled是false去配置文件里打开即可。4.4 Redis 没起来的时候验证码接口是什么表现这是我当天最后一个、也是最隐蔽的一个坑。Redis 服务其实没启动但验证码接口返回的是 500前端表现却像是加载不出来我一开始以为是网络问题。验证码的答案必须落在一个地方让后端校验时取回若依用的是 Redis。Redis 连不上时生成验证码这一步就会抛异常。所以先确认 Redis 进程在跑redis-cli ping返回PONG才算正常再确认后端配置里 Redis 的 host、port、password、database 都对如果 Redis 有密码而配置里漏了 password某些版本下会表现为连接超时而不是报错排查时容易误判成网络问题。提示验证码相关的异常在后端日志里通常会有一个明显的堆栈关键字是RedisConnectionFailureException或者Unable to connect to Redis。如果日志里搜不到这类关键字就去查 Nginx 的error.log看请求到底有没有到后端。两边的日志配合着看能省下大量猜测时间。4.5 代理路径多一个斜杠的经典事故最后说一个小到不能再小、但真的会卡住人的问题。看这两行配置# 正确 proxy_pass http://127.0.0.1:8080/; # 错误 proxy_pass http://127.0.0.1:8080 //;或者更隐蔽的一种location 写成了/prod-api//多打了一个斜杠请求/prod-api/captchaImage压根匹配不上这条规则直接掉到location /里去找静态文件404。Nginx 的 location 前缀是严格按字符串匹配的多一个字符都不行。我在排查时养成了一个习惯把 Nginx 配置里所有 location 行单独 grep 出来看一遍肉眼核对前缀和斜杠十几秒的事比在浏览器里反复刷新强。5. 后端接口404proxy_pass 结尾斜杠、context-path 与路径截断如果说前面几个坑还算是部署常识那这一块就是真正容易反复踩的地方。因为它的表现和配置只差一个字符但原因却完全不同。5.1 proxy_pass 带斜杠和不带斜杠差的是一个前缀这是 Nginx 反向代理里最经典、也最容易忘的规则。用一张表说清楚location 写法proxy_pass 写法请求/prod-api/login实际转发到/prod-api/http://127.0.0.1:8080/http://127.0.0.1:8080/login✅/prod-api/http://127.0.0.1:8080http://127.0.0.1:8080/prod-api/login/prod-apihttp://127.0.0.1:8080/http://127.0.0.1:8080//login或路径错乱 ❌规律是这样的如果proxy_pass的地址带 URI也就是结尾有个/或具体路径Nginx 会用这个 URI 替换掉 location 匹配到的前缀如果不带 URI结尾没有斜杠就把完整原始路径原样转发。所以正确写法是 location 和 proxy_pass 都以斜杠结尾这样/prod-api/被替换成/剩下的login拼上去后端收到/login——正好对上后端的路由。这个规则我第一次看也是一脸懵后来是这么记的把proxy_pass想象成我要把匹配到的那一段剪掉接上我写的这一段。写了/就是剪掉后接一个/什么都不写就是不剪。这样就不会搞混了。另外一个细节location /prod-api不带末尾斜杠时/prod-api123/xxx这种路径也会被匹配上因为它是前缀匹配。所以 location 后面一定要加斜杠把匹配范围收窄。5.2 后端 context-path 与前端 BASE_API 必须对齐如果你的后端在application.yml里设置了server: port: 8080 servlet: context-path: /ruoyi那么后端真实的接口路径就变成了/ruoyi/login、/ruoyi/captchaImage。这时候 Nginx 转发必须补上这一段location /prod-api/ { proxy_pass http://127.0.0.1:8080/ruoyi/; }注意proxy_pass结尾的斜杠不能丢否则会拼成/ruoyi/brlogin这样的错乱路径。若依官方模板默认是不设 context-path的或者设成/这也是为什么很多教程里的配置直接就proxy_pass http://127.0.0.1:8080/;。但很多公司出于统一规范会给后端加一个前缀这时候前端和 Nginx 都得跟着改。我踩的坑就在这里后端同事为了和公司其他服务保持一致把context-path改成了/api但前端.env.production里的VUE_APP_BASE_API还是/prod-apiNginx 也没补上/api。结果就是请求转发到后端后端一看路径是/login而它只认/api/login直截了当返回 404。提示判断是不是这个问题有个特别快的办法——直接在服务器上 curl 后端curl -i http://127.0.0.1:8080/login和curl -i http://127.0.0.1:8080/api/login各试一次看哪个能通。能通的那个就是后端真实路径然后倒推 Nginx 该怎么写。这一步能省掉大量在配置文件里来回改的时间。5.3 微服务版本网关前缀与路由断言的坑如果你用的是若依微服务版链路又多了一跳Nginx → 网关通常是 8080→ 具体微服务。这时候 404 可能出现在网关这一层。微服务版的网关路由配置大概是这样的spring: cloud: gateway: routes: - id: ruoyi-system uri: lb://ruoyi-system predicates: - Path/system/**前端发/prod-api/system/user/listNginx 剥掉/prod-api/网关收到/system/user/list命中Path/system/**这条断言转发给ruoyi-system服务。这里要注意的是若依微服务的网关默认不会自动去掉/system这个前缀它靠服务名和路径约定来区分服务内部接口本身就是/system/user/list。如果你在网关路由里加了StripPrefix1过滤器路径就变成了/user/list服务里找不到这个路由404。我在帮另一个朋友排查时还遇到过一种情况Nginx 的location写成了/prod-api/但微服务版前端发出去的前缀其实是/prod-api末尾没斜杠因为 axios 拼接方式不同两边差一个字符匹配不上请求落进静态资源规则里报的是 HTML 格式的 404。这种问题看响应体一眼就能分辨。5.4 用 curl 分层验证三步定位断在哪一跳我后来总结出一个特别高效的定位方法五分钟内能锁定问题在哪一层。核心就是从后端往前往前推每层单独验证第一步绕过 Nginx直接打后端curl -i -X POST http://127.0.0.1:8080/login \ -H Content-Type: application/json \ -d {username:admin,password:admin123}如果这一步就 404说明是后端路径问题context-path 或端口不对跟 Nginx 无关。第二步通过 Nginx 打但用 Host 头指定curl -i http://127.0.0.1/prod-api/captchaImage -H Host: 你的域名如果第一步通、第二步不通问题就锁定在 Nginx 的 location 或 proxy_pass 上。第三步看日志。Nginx 的access.log能看到请求打到了哪个 location、返回了什么状态码后端的日志能看到它收到了什么路径。两条日志的时间戳对一下链路就清楚了。tail -f /var/log/nginx/access.log tail -f /home/ruoyi/logs/sys-info.log这三步做完如果你还没找到原因那大概率不是配置问题而是你看的配置文件不是实际生效的那份——这种情况真的存在比如nginx -s reload加载的是/etc/nginx/nginx.conf而你改的是/usr/local/nginx/conf/nginx.conf。用nginx -t确认配置文件路径别嫌麻烦。6. 那些藏在404背后的次生问题跨域、413、WebSocket前面几个坑解决完系统基本能跑起来了。但在实际使用中还会陆续冒出一些看起来像 404、其实完全不是的问题。这一节讲的都是我自己踩过或者在别人项目里见过的。6.1 已经走代理了为什么还会跨域按理说前端请求/prod-api/xxx是同源请求Nginx 转发是服务端到服务端不存在浏览器的同源策略问题。但如果你把前端配置改成了直连后端地址比如VUE_APP_BASE_API http://192.168.1.100:8080那浏览器就会直接向 8080 发请求这就是真跨域了。而且此时若依的 token 是存在 Cookie 里的跨域情况下 Cookie 的SameSite默认策略会把它拦下来导致登录成功但后续接口全部 401或者干脆就是预检请求 OPTIONS 返回 404。我的建议很明确生产环境前端一定要走同域名的/prod-api/前缀让 Nginx 做代理不要图省事直连后端 IP。这样既避免了跨域也避免了暴露后端端口顺便还能在 Nginx 这一层做限流和日志。6.2 上传附件返回 413别当成404查若依的文件上传接口是/prod-api/common/upload。Nginx 默认的请求体大小限制是 1MB超过就直接拒绝返回 413。但如果前端做了统一的错误处理可能会把它显示成请求失败让人误以为是接口 404。解决办法是在server块或location块里加一行client_max_body_size 20m;这个值要和后端 Spring 的spring.servlet.multipart.max-file-size以及前端 axios 的超时时间配合着设。我见过最离谱的情况是 Nginx 设了 20mSpring 还是默认的 1MB结果 5MB 的文件传到一半报错排查方向完全跑偏。顺带提一句上传大文件时还要注意proxy_read_timeout默认 60 秒大文件慢网络下很容易超时。我在配置文件里一般会设成 300 秒。6.3 WebSocket 握手失败与 Upgrade 头如果你的若依项目接了实时消息、日志推送之类的功能走 WebSocket那 Nginx 还需要额外配置location /websocket/ { proxy_pass http://127.0.0.1:8080/websocket/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 3600s; }少这两行Upgrade/Connection头握手就会失败。而握手失败在浏览器里的表现有时候是 404有时候是 400取决于具体实现。判断方法还是看响应体如果是 Nginx 返回的多半是 location 没配上如果是后端返回的那就要看后端有没有开启 WebSocket 支持、路径有没有写对。proxy_read_timeout这一项特别容易忘。默认 60 秒WebSocket 连接空闲 60 秒后就会被 Nginx 主动断开前端表现就是消息推送一会儿有一会儿没有或者不停地重连。设成 3600 秒基本能覆盖绝大多数场景。7. 我自己的一套排查顺序和可直接抄的配置写到这儿该讲的坑基本都讲完了。最后把我当天下午摸索出来的排查顺序和最终的配置文件整理一下你部署的时候可以直接照着走一遍。7.1 十分钟定位法从浏览器 Network 到后端日志遇到 404 别慌按这个顺序走基本十分钟能定位看 Network 里那条 404 的响应头。有Server: nginx就是 Nginx 没转发没有就是后端返回的。这一步决定后面往哪个方向查。如果响应体是 Nginx 的 404 页面核对三件事请求 URL 的前缀和 location 是否完全一致包括斜杠、proxy_pass是否带 URI、配置文件是不是实际生效的那份用nginx -t确认。如果响应体是后端 JSON说明转发成功了问题在后端。先 curl 后端真实端口确认 context-path 和接口路径再回头改 Nginx 的转发目标。看日志。/var/log/nginx/access.log里能看到请求打到了哪个 location后端日志能看到它实际收到的路径。两边时间戳一对链路立刻清晰。改完配置记得nginx -t nginx -s reload。nginx -t会告诉你配置有没有语法错误这一步千万别跳过直接 reload 失败会让服务继续跑旧配置你会以为改动没生效然后在错误的方向上越走越远。这五步里第四步是最容易被跳过的但它往往是最快的。我这次最后那个 Redis 的问题就是靠后端日志里的一句连接超时提示定住的前后不到两分钟。7.2 一份可以直接抄的配置这是我最终跑通的配置前后端分离、Vue2 版若依、后端 8080 端口。Vue3 版的差异我在下面单独标注。server { listen 80; server_name your.domain.com; charset utf-8; client_max_body_size 20m; # 1. 前端静态资源 history 路由兜底 location / { root /data/web/ruoyi-ui/dist; index index.html index.htm; try_files $uri $uri/ /index.html; } # 2. 接口反向代理前缀替换后转发给后端 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_connect_timeout 30s; proxy_read_timeout 300s; proxy_send_timeout 300s; } # 3. 上传文件访问 location ^~ /profile/ { alias /home/ruoyi/uploadPath/; } }对应的前端.env.production# Vue2 版 VUE_APP_BASE_API /prod-api # Vue3 版Vite # VITE_APP_BASE_API /prod-apiVue3 版还需要在vite.config.js里确认base的值以及build.outDir是不是dist。Vite 打包出来的静态资源目录名由assetsDir控制默认是assets若依模板一般会改成static保持一致这个别改错。配置里有几个地方是我特意加上的^~前缀确保接口请求不会被正则规则抢走client_max_body_size 20m让上传不报 413超时时间放大到 300 秒避免导出大报表时断连。这些都是实际用起来才会发现的细节配置的时候顺手写上能省掉后面很多麻烦。7.3 上线前对着这个清单过一遍最后把我自己的自检清单列出来部署完成后花五分钟逐项确认能挡掉绝大多数问题前端.env.production里的 API 前缀和 Nginx 的 location 前缀逐字符一致包括斜杠Nginx 的root指向的目录里index.html和static/是同级try_files的兜底写的是/index.html不是index.htmlproxy_pass结尾带/实现前缀替换后端 context-path 如果是/xxxproxy_pass结尾要写成/xxx//profile/用 alias 映射到上传目录结尾斜杠别漏服务器上nginx -t通过配置路径和后端实际访问的保持一致Redis 是活的redis-cli ping有回应验证码接口能返回 base64 图片不是空字符串权限确认Nginx 运行用户对静态目录有读权限。我自己的体会是这类部署问题 90% 不是技术难而是细节多。同一个 404 状态码背后可能是五种完全不同的原因唯一的解法就是把链路拆开、一层一层验证。等这套流程走过两三遍再遇到新的 404你打开 Network 看一眼响应头就能大致知道该往哪查了。这种直觉比记住多少条配置规则都管用。
返回列表