Vue3项目部署Nginx全攻略:白屏排查、路由配置与反向代理实践

发布时间:2026/10/7 11:41:17

Vue3项目部署Nginx全攻略:白屏排查、路由配置与反向代理实践 1. 部署思路与路线选择为什么你的 Vue3 项目本地好好的一上服务器就白屏先说个绝大多数人都会踩的坑本地npm run dev跑得飞起打包后扔到 Nginx 上结果打开页面白屏、控制台一堆 404、静态资源全部找不到。这个问题的根源99% 都在打包配置和Nginx 的 location 匹配机制这两件事上。Vue3 Vite 构建出来的产物是一堆静态文件HTML、JS、CSS、图片Nginx 的作用就是把这些文件以最快的速度吐给浏览器。但前端路由分为 history 模式和 hash 模式如果你用的是 history 模式地址栏没有#那 Nginx 必须做一步“把所有未知路径都指向 index.html”的操作否则刷新页面就会 404。在动手之前我先说下路线选择。我在实际项目里遵循的原则是开发环境用 Vite 自带的 dev server代理跨域全交给server.proxy配置。生产环境用 Nginx 托管静态文件 反向代理后端接口。不推荐在 Node 环境里再跑一个静态服务那样既浪费资源也失去了 Nginx 处理高并发静态请求的能力。多环境部署本地虚拟机、测试服务器、生产服务器至少准备三套 Nginx 配置模板用注释统一管理避免每次上线临时改配置改出事故。这套路线的核心逻辑很简单谁擅长什么就干什么。Vite 擅长构建和热更新Nginx 擅长托管静态文件和做反向代理各司其职。2. 环境准备与参数选型Nginx 装哪个版本、怎么确认编译参数2.1 Nginx 版本选择与安装方式我见过很多新手在 Windows 上下载 Nginx 折腾半天最后发现很多生产环境特性比如gzip_static、http_v2根本没编译进去。我在生产环境只推荐两种方式CentOS/RHEL 系用官方 Nginx 源nginx.org的 yum 源安装不要用系统自带的旧版本。Ubuntu/Debian 系用nginx.org官方源或者直接编译安装。用 apt 默认源装的版本往往偏旧而且模块不全。我一般会在装完后跑一条命令确认关键模块nginx -V 21重点看有没有这几个参数--with-http_v2_moduleHTTP/2 支持多站点部署时性能差异明显。--with-http_gzip_static_module预压缩 gzip提前把.gz文件准备好减少 CPU 消耗。--with-stream如果需要做 TCP/UDP 转发这个必须有。--with-http_ssl_moduleHTTPS 必须没有它你的证书配置全白搭。2.2 目录结构与配套工具部署涉及的文件要分开管理不要一股脑塞进 Nginx 的 HTML 目录。我常用的结构是这样的/www/ ├── dist/ # 前端打包产物 ├── upload/ # 用户上传文件交给 Nginx 直接访问 └── nginx-conf/ # 各环境 Nginx 配置备份配合工具我强烈推荐在本地装上Postman或curl调接口时定位问题速度快得多。另外开发机的/etc/hosts也要改一下把测试域名解析到虚拟机或服务器 IP这样能尽早暴露 cookie 跨域、域名白名单这类问题。3. 前端打包与配置Vite 的 base 路径是白屏的罪魁祸首之一3.1 理解 base 参数的作用很多 Vue3 项目部署在根路径比如https://example.com/那你可能永远碰不到这个问题。但只要你想把应用部署在子路径下比如https://example.com/admin/就必须理解base。Vite 的base配置决定了打包后 HTML 里引用 JS、CSS 的路径前缀。默认是/这时候生成的资源地址是script src/assets/index.abc123.js/script如果 Nginx 里把站点 root 指到了dist目录浏览器请求/assets/index.abc123.js就能命中。但如果你把应用部署在/admin/下浏览器请求的却是/assets/...那 Nginx 一定返回 404。这种情况要么把 Vite 的base设为/admin/要么在 Nginx 里做特殊处理。我的建议是能用根路径部署就别用子路径。子路径带来的麻烦远比你想象得多。如果你确实要部署在子路径vite.config.js里这样写export default defineConfig({ base: process.env.NODE_ENV production ? /admin/ : /, // 其他配置... })打包后你会在dist/index.html里看到script typemodule crossorigin src/admin/assets/index.abc123.js/script3.2 打包命令与环境变量Vue3 项目通常会区分.env.production和.env.test。我建议在package.json里把打包命令写明别让人用默认的npm run build猜环境{ scripts: { build:test: vite build --mode test, build:prod: vite build --mode production } }打包完成后第一件事就是查看dist/index.html。有两种情况资源路径是绝对路径/assets/xxx.js站点只能部署在域名根路径。资源路径是相对路径./assets/xxx.js配置比较灵活但vue-router如果用的是 history 模式相对路径配合子路径反而容易出问题需要谨慎。我实测下来生产环境用根路径 base: /最稳。如果想省事也可以把base设置为./打包出相对路径这样直接把 dist 扔到任意目录都能跑。但这个做法有一个隐患如果路由懒加载的 chunk 在跳转时加载相对路径的解析可能出问题尤其是嵌套路由时。所以我还是坚持用绝对路径 根路径部署的方案。3.3 确认产物完整性打包后至少确认这几样东西存在dist/index.htmldist/assets/目录里有 JS 和 CSSdist/favicon.ico如果有的话dist/public/里的静态资源如果有的话有些朋友会把 public 目录和 assets 搞混。Vite 中public目录下的文件会被原样复制到 dist 根目录而src/assets下的文件会被打包成带 hash 的文件。部署后如果你发现某个图片 404先想清楚它是从哪个目录引入的。4. Nginx 配置拆解静态托管、history 路由与 SPA 降级4.1 一份能直接抄的 Nginx 配置先给一份我在单站点部署时最常用的配置注释我会逐行解释server { listen 80; server_name example.com www.example.com; # 前端静态资源根目录 root /www/dist; index index.html; # 上传文件目录可选 location /upload/ { alias /www/upload/; } # API 反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; 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; } # 静态资源缓存策略 location /assets/ { expires 7d; add_header Cache-Control public, immutable; } # history 路由降级 location / { try_files $uri $uri/ /index.html; } }这份配置核心就三件事4.2 history 路由为什么必须配 try_filesVue3 的vue-router在 history 模式下URL 路径是由前端 JS 管理的服务器文件系统里根本不存在对应的物理文件。比如你访问/user/123服务器去找/user/123这个文件结果是 404。try_files $uri $uri/ /index.html的意思是先按当前的 URL 找对应文件。找不到再找对应目录。还找不到就返回index.html。这样浏览器加载index.html后Vue Router 会根据当前地址正确渲染对应组件。location /这个块覆盖了所有未被其他 location 匹配的请求是 history 模式必须的兜底逻辑。4.3location /assets/的缓存策略把带 hash 的静态资源单独设一个 location设置长时间缓存。这里的.hash文件只要内容不变文件名也不变浏览器可以放心缓存一年。我的习惯是location /assets/ { expires 30d; add_header Cache-Control public, immutable; }而index.html本身要禁止缓存否则每次发版本用户看到的还是旧页面。可以用location /index.html { add_header Cache-Control no-cache, no-store, must-revalidate; }这个location 是精确匹配优先级最高专门处理 index.html。4.4 多站点多端口配置方式很多人问“本地 虚拟机多端口 Nginx 多站点自定义域名怎么配”核心思路就是一个 IP 上跑多个 server 块靠server_name区分域名server { listen 80; server_name dev1.example.com; root /www/dev1-dist; } server { listen 80; server_name dev2.example.com; root /www/dev2-dist; }本地开发时把这两个域名都解析到虚拟机 IP改/etc/hosts。如果同一个域名下分端口访问也可以这样server { listen 8081; server_name example.com; root /www/app1-dist; } server { listen 8082; server_name example.com; root /www/app2-dist; }这种方式适合在本地虚拟机上模拟多个站点的部署环境尽量保持和生产环境一致减少“本地能跑上线就挂”的尴尬。5. 反向代理与跨域接口调不通先查 proxy_pass5.1 proxy_pass 后面带不带斜杠的区别这是 Nginx 配置里最容易翻车的地方没有之一。# 情况一不带斜杠 location /api/ { proxy_pass http://127.0.0.1:8080; } # 情况二带斜杠 location /api/ { proxy_pass http://127.0.0.1:8080/; }区别在于请求路径的替换规则不带斜杠/api/user/list原封不动转发给后端后端拿到/api/user/list。带斜杠/api/user/list中匹配 location 前缀的部分会被替换为/后端拿到的是/user/list。如果你的后端接口本来就定义在/api下那就用不带斜杠的。如果后端接口路径没有/api前缀就得用带斜杠的把location /api/这一层剥掉。我在实际项目里踩过最深的坑就是带斜杠替换后后端接口变成了//user/list多一个斜杠导致路由匹配失败。5.2 WebSocket 与 HTTP 升级头Vue3 项目跟后端建立 WebSocket 连接很常见反向代理如果不加升级头WebSocket 握手一定会失败。加上这一段location /ws/ { proxy_pass http://127.0.0.1:8080/ws/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_read_timeout 300s; }proxy_http_version 1.1是必须的HTTP/1.0 不支持 Upgrade 头。proxy_read_timeout设长一点避免长连接频繁断开。5.3 代理缓冲区导致的事件流中断SSE服务端推送是另一个容易出问题的点。proxy_buffering默认是开启的如果不关掉客户端可能迟迟等不到数据。配置location /sse/ { proxy_pass http://127.0.0.1:8080; proxy_buffering off; proxy_cache off; }我遇到过前端 EventSource 连接后一直不触发 message 事件的情况一开始还以为是代码问题查了半天没想到是 Nginx 缓冲导致的。后来把proxy_buffering off加上立刻正常。5.4 防跨域配置如果你不在 Nginx 层做代理而是前端直接请求其他域名的接口那必须处理 CORS。但我的建议是能用反向代理解决的问题不要用跨域解决。如果实在要用 CORSNginx 里这样加add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Credentials true; add_header Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS; add_header Access-Control-Allow-Headers Authorization, Content-Type, X-Requested-With; if ($request_method OPTIONS) { return 204; }注意这里的$http_origin比写死*要靠谱支持携带 cookie 的请求。Access-Control-Allow-Credentials和*不能共存用$http_origin是标准做法。6. 踩坑实录location 匹配顺序、404 与跨域疑难杂症6.1 location 匹配优先级你踩过几个坑Nginx 的 location 匹配顺序非常反直觉很多人栽在这里。我用最直白的方式说明优先级从高到低精确匹配。比如location /index.html。^~前缀匹配一旦命中就停止再找正则。~或~*正则匹配按配置文件顺序。普通前缀匹配最长匹配优先。我举个真实案例。有个项目配置了location /api/ { proxy_pass http://127.0.0.1:8080; } location ~* \.(js|css|png|jpg)$ { expires 7d; }结果/api/user返回的是 JS 文件的内容不会因为/api/是精确前缀正则只匹配静态文件后缀。但如果你的 API 路径里恰好包含.jsonlocation ~* \.(json)$ { # 静态文件处理 }那/api/config.json就可能被正则命中而不会走到/api/的反向代理。这就是为什么我强烈建议API 路径别带文件后缀或者对 /api/ 用^~强制前缀优先location ^~ /api/ { proxy_pass http://127.0.0.1:8080; }^~的意思是这个前缀匹配优先级高于正则但低于精确匹配。加了^~之后正则里的文件后缀规则就不会劫持 API 请求了。6.2 刷新 404 但首页能进问题出在 try_files先说现象点首页能进一刷新就 404。这个就是典型的 location 顺序问题。我见过有人把静态资源 location 放在最前面把location /放在最后但用的是location / { root /www/dist; }没有try_files刷新自然 404。正确做法是把try_files加到/的 location 中。另外有个细节try_files $uri $uri/ /index.html最后一项不是$uri/index.html而是根路径下的 index.html。如果你部署在子目录这里也要改成实际路径。6.3net::ERR_CERT_COMMON_NAME_INVALID与 HTTPS 证书这个报错几乎都是证书域名不匹配导致的。最典型的情况证书是给example.com签的但访问用的是 IP 或www.example.com。证书是自签名证书浏览器不信任。Nginx 配置的证书路径写错了加载了旧证书。排查思路openssl s_client -connect example.com:443 -servername example.com 21 | grep subject看看证书的 CN 和 SAN 是否覆盖了访问域名。如果生产环境真的想省事推荐用 Lets Encrypt 这种免费证书配合自动续期。开发环境不想用自签名可以用 mkcert 生成本地信任的证书CtrlC 后在虚拟机或本机测试体验好得多。6.4 Windows 下查看 Nginx 日志的实用技巧Windows 上运行 Nginx日志默认在 Nginx 安装目录的logs/error.log和logs/access.log。有几种方式实时查看用 Notepad 打开日志文件它会提醒文件已变化并重新加载。用 PowerShell 的Get-Content -Wait logs/error.log实现 tail 效果。用跨平台的日志查看工具比如一些轻量级 GUI 工具。我踩过最大的坑是Windows 上 nginx.conf 被修改后nginx -s reload提示成功但实际没有重新加载。解决方法是先nginx -t测试配置再nginx -s reload。如果还不行就nginx -s stop后重新nginx。我遇到过一次因为端口被占用导致 reload 失败的情况结果老配置还在跑问题半天没定位出来。6.5welcome to nginx!页面怎么解决这个页面出现说明你访问的服务器上确实装了 Nginx但没有匹配到你期望的站点配置。常见原因只修改了默认配置但server_name跟访问域名不匹配。配置文件没加载改了主配置但忘了 include 站点目录。浏览器缓存了旧页面。排查思路nginx -t # 通过 curl 查看响应头 curl -I http://localhost如果返回的页面还是默认页检查一下 Nginx 加载的配置文件里有没有残留的默认 server 块。有种情况很隐蔽新配置写在conf.d/下但主配置文件里没有include /etc/nginx/conf.d/*.conf这句。Ubuntu 的包管理装的 Nginx 默认有编译安装的默认没有很容易漏。7. 线上故障排查清单与小结我把几个高频问题的排查顺序整理成了一张表方便你对照操作现象可能原因排查顺序页面白屏base 路径配置错误、路由模式不匹配先看控制台请求 URL再查 dist/index.html 引用路径刷新 404缺少 try_files 配置看 nginx.conf 的 location / 块接口 404proxy_pass 斜杠问题用 curl 直接请求后端接口确认是否可用接口 401/403代理头信息缺失检查 Host、Referer、Origin 头传递资源 404root/alias 路径错误确认 root 指向 dist不是 dist/distWebSocket 断开缺少 Upgrade 头抓包看握手响应页面加载慢缺少缓存配置、没开 gzip先开 gzip再配置 assets 长缓存证书报错证书域名不匹配openssl 检查证书 SAN关于 root 指向多说一句。很多朋友把 dist 目录放在了 Nginx 的 html 目录下然后配置root /usr/local/nginx/html/dist;这样浏览器访问/时找的是/usr/local/nginx/html/dist/index.html如果这个文件存在就没问题。但如果你配置成了root /usr/local/nginx/html/那访问/时会去找html/index.html而 index.html 可能在 dist 里就 404 了。这就是“dist/dist”的经典错误检查路径时先确认下。最后我踩过这么多次坑最大的体会就是每次改配置前先备份再nginx -t最后才 reload。这三个习惯能帮你避免 90% 的线上事故。有一段时间我一上生产就紧张后来养成了一套固定的发布流程先跑npm run build把 dist 传到服务器然后备份旧的 dist再替换再验证资源路径。这套流程走熟了之后无论前端项目怎么换心里都有底。
延伸阅读

更多相关文章

2026/10/7 11:41:17

风电行业MES实施实战:2D条码、OPC独立部署与柔性生产中枢构建

简介:本资源是金风科技MES系统落地实践的完整经验总结报告,面向制造业数字化转型从业者、MES实施顾问、工业信息化项目负责人及智能制造领域学习者,聚焦风电装备行业多品种小批量生产场景下的柔性制造与质量追溯难题。文档为单个PDF文件&…

2026/10/7 11:36:16

C++标准库search与search_n:序列查找与连续重复检测一次讲透

日志告警收敛那阵子&#xff0c;我在几十万行日志里找连续出现3次的 ERROR 码。同事第一版是手写双层循环&#xff0c;外层记录起始位置&#xff0c;里层用计数器去点算&#xff0c;代码勉强能跑&#xff0c;但换个容器类型就得重写一遍。后来我换成 <algorithm> 头文件…

2026/10/7 11:36:16

嵌入式电源路径保护:TPS259483与ATmega6450的数字化方案

做嵌入式项目这些年&#xff0c;我越来越觉得“电源路径保护”是一条从入门到进阶的分水岭。很多同学把主控代码调通了&#xff0c;板子也点亮了&#xff0c;却在电源入口放一颗保险丝、加一颗TVS就宣布收工。短时间看不出问题&#xff0c;等到了工业现场、车载环境&#xff0c…

2026/10/7 12:36:24

Shopify RN回迁原生:AI驱动的跨端技术债治理实践

1. 这不是“技术倒退”&#xff0c;而是一次精准的工程价值重校准Shopify把用了好几年的React Native应用&#xff0c;在12周内全量迁回Swift&#xff08;iOS&#xff09;和Kotlin&#xff08;Android&#xff09;——这消息刚出来时&#xff0c;我朋友圈里一半人说“终于清醒了…

2026/10/7 12:36:24

CD4066模拟开关构建音频二选一电路:原理、布局与调试详解

1. 确认使用场景&#xff1a;音频切换为什么能用CD40661.1 从“模拟开关”的本职说起很多入门玩家第一次看到CD4066&#xff0c;都是被“四路模拟开关”这几个字带进来的。它内部有四个互相独立的开关&#xff0c;每个开关由一个控制引脚决定导通还是断开&#xff0c;但它不是继…

2026/10/7 12:36:24

持久状态不等于可信恢复:容器快照与一致性恢复的关键

1. 快照并非保险&#xff1a;持久状态和可信恢复之间到底隔着什么先说结论&#xff1a;在Cloudflare Containers这类边缘容器平台上&#xff0c;很多人对"快照"的预期是——我定期把容器状态存下来&#xff0c;出故障时一键恢复&#xff0c;业务无损。这个预期在90%的…

2026/10/7 12:36:24

容器快照恢复不等于可信恢复:状态一致性实践指南

先别急着把“能恢复”当成“恢复好了”。这是我在折腾 Cloudflare Containers 快照功能时最大的感悟。作为一款主打“冷启动低于 600ms、热启动低于 150ms”的容器产品&#xff0c;快照机制确实是它的核心竞争力之一&#xff0c;但快照恢复的“成功”和业务状态的“正确”是两码…

2026/10/7 12:31:23

NE5532实战指南:经典运放的现代工程价值

1. 为什么今天还要折腾NE5532&#xff1f;——一个被低估的“模拟电路活化石”你可能在B站看到过那种视频&#xff1a;镜头扫过一块布满跳线、焊点发亮的洞洞板&#xff0c;背景音是稳压电源“滋滋”的轻微啸叫&#xff0c;画外音说&#xff1a;“老司机带你玩转NE5532”。弹幕…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起&#xff1a;为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高&#xff0c;很多人第一次听到会以为是某个新模型的名字&#xff0c;其实它更像是一种思路——把Jev模型的能力当作底座&#xff0c;通过Agent的方式去接管浏览器&#xf…

2026/10/7 8:18:33

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同"&#xff1a;多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西&#xff0c;大概率会有一种感觉&#xff1a;单个 Agent 能做的事情&#xff0c;其实很快就摸到天花板了。你给它一个提示词&#xff0c;挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑