SpringBoot+Vue前后端分离项目部署:宝塔面板+Nginx反代完整指南 前后端分离项目写代码的人很多真到了部署上线这一步翻车的也不少。SpringBoot Vue 这种组合在开发环境跑得欢一旦要搬到服务器上数据库、端口、反向代理、跨域、路由 history 模式全部搅在一起任何一个环节出问题页面就是白屏、接口就是 404。我自己前后部署过几个这样的项目踩过的坑比写业务代码时遇到的 bug 还多。这篇东西就把一套完整的宝塔面板部署方案拆开揉碎讲清楚从服务器初始化、后端 jar 包部署、前端 Nginx 托管到跨域转发、安全组放行、HTTPS 证书配置全部走一遍。不管你是刚毕业的学生还是公司里被赶鸭子上架的全栈照着我这套流程一步步来基本能稳稳上线。我选宝塔面板来部署不是因为懒而是因为它把环境安装、站点配置、证书签发、进程守护这些事集中在一个界面里省下来的时间够你调好几个 bug。但宝塔只负责把事做简单背后的原理你还是要懂否则出了问题连排查方向都没有。下面我就按一次完整的上线流程来写内容包括架构设计、环境准备、后端部署、前端部署、跨域处理、HTTPS 配置和常见故障排查内容很长建议收藏后对着操作。1. 部署方案设计为什么是宝塔 Nginx 反向代理1.1 前后端分离项目的部署思路前后端分离的本质是两个独立的应用SpringBoot 后端是一个跑在 JVM 里的 Java 进程监听某个端口比如 8080Vue 前端是一堆静态文件JS、CSS、HTML理论上扔到任何 Web 服务器上都能跑。但“能跑”不等于“能用”因为前端页面要请求后端接口这就牵扯出两个问题接口地址配置和跨域。开发环境里Vue 项目通过 Vite 或 Vue CLI 的 proxy 配置把 /api 开头的请求转发到后端 localhost:8080天然不存在跨域。但打包之后静态文件由 Nginx 托管没有 proxy 帮你转发了浏览器直接向服务器发请求跨域问题立刻浮现。很多人在这里卡住然后跑去后端的 Controller 上加一堆 CrossOrigin 注解或者写一个全局 CORS 配置类虽然也能解决但这种方案在生产环境里不是最优解。我的方案是把宝塔的 Nginx 当作“总入口”同时托管前端静态文件和后端反向代理。Nginx 监听 80/443 端口用户访问域名时加载 Vue 的静态资源当请求路径命中 /api 前缀时Nginx 把请求转发给本机 8080 端口的 SpringBoot 进程。这样就实现了同源访问浏览器根本感觉不到跨域的存在前端代码里也不需要写死任何服务器 IP 或端口。这个方案的好处很明显前端和后端共用一个域名、一个端口不需要把后端接口暴露到公网减少了被扫描攻击的风险Nginx 本身处理并发连接的能力比 Java 内嵌的 Tomcat 强静态资源请求根本到不了后端将来要是做负载均衡Nginx 改几行配置就能多转发几台后端节点。1.2 整体架构与请求流转拿我这次部署的模拟项目 X 来说架构非常简单一条链路就讲清楚了用户浏览器 → 域名80/443 → 宝塔 Nginx → /api 开头的请求转发到 127.0.0.1:8080SpringBoot jar 包 ↓ / 开头的其余请求返回 Vue 静态文件dist 目录里的 index.html画成文字可能不够直观我拆开说。用户的浏览器输入域名后DNS 解析到服务器 IP请求到达 Nginx。Nginx 先看 URL 路径如果路径是 /js/app.js、/css/style.css 这类静态资源直接去服务器磁盘上读文件返回如果路径是 /api/user/login 这种带 /api 前缀的就交给反向代理模块转发到 SpringBoot 应用。SpringBoot 处理完业务逻辑把 JSON 数据返回到 NginxNginx 原样回给浏览器。整个过程浏览器只和你的域名打交道同源策略满足跨域问题从根上就没了。端口规划也很重要。我的习惯是后端 SpringBoot 进程只绑定 127.0.0.1即服务端口的 host 写成 127.0.0.1然后用 Nginx 转发这样公网根本访问不到 8080 端口只有通过域名 80/443 进来的请求才能到达后端。如果你的服务器配置了宝塔防火墙和云厂商安全组那么安全组只需要放行 80 和 4438080 这种内部端口留在内网通信就好。这个设计后面我会反复强调因为它真的能挡掉不少乱扫端口的恶意流量。2. 部署前准备服务器环境与项目打包2.1 服务器与宝塔面板初始化开始部署之前先把基础环境搞定。我这次用的是一台便宜的云服务器操作系统选了主流的 Linux 发行版具体买多大配置根据你的项目体量来。一个简单的经验SpringBoot 单应用 MySQL 这种组合2C4G 起步比较稳别贪便宜买 1G 内存的机器Java 进程加 MySQL 一起跑内存很快就爆。部署前可以用命令看下系统版本和资源情况确认没问题再继续。服务器的环境我直接用的宝塔面板手动安装方式。这里多说一句宝塔面板有两种安装风格一是“编译安装”耗时更长但性能更好全看服务器和网络情况二是软件商店里的一键安装。我通常选择极速安装实际使用中两者差异不大省下的时间更重要。进入宝塔后台之后第一件事是在软件商店里安装 Nginx、MySQL、PHPPHP 不一定用到但宝塔的默认环境检测会要求装装不装看你自己的需求以及一个很重要的工具进程守护管理器。Java 后台进程用它来托管比裸 nohup 命令可靠得多。装好环境后要处理两项安全设置一是修改 SSH 端口和宝塔面板的默认端口这个每个人有自己的偏好我不展开二是设置宝塔面板的安全组规则确认放行了 80、443 等端口否则后面域名访问不了排查半天发现其实是端口没放行就非常浪费时间了。2.2 SpringBoot 后端项目打包后端打包前先检查配置文件。我在开发环境用的 application-dev.yml 里数据库地址写的是 localhostRedis 地址写的也是 localhost端口 8080。生产环境如果直接套用这份配置部署完启动后连不上数据库又得调半天环境变量。我的习惯是创建一份生产专用的 application-prod.yml把数据库连接、Redis、文件上传路径等全部调整成服务器上的实际值然后在 application.yml 里激活 prod 配置。打包命令很简单在项目根目录执行mvn clean package -DskipTests跳过单元测试不是不重视质量而是因为服务器上没有测试环境依赖的组件很多测试用例在打包阶段根本跑不起来白白浪费几分钟构建时间。打出来的包在 target 目录下一般是 xxx.jar。如果你的项目用 Gradle对应命令是 gradle bootJar。打包完成后用 SSH 工具上传到服务器的某个目录比如 /www/wwwroot/project-name/backend 下。我习惯把所有项目文件都放在宝塔的网站根目录管理范围内后面配置站点时路径更清晰。这里有个容易踩的坑SpringBoot 内置 Tomcat 默认是打 jar 包但有些老项目配置了packagingwar/packaging这种项目部署方式完全不同得用外置 Tomcat。部署前看一眼 pom.xml 里的 packaging 节点别盲目照着别人的教程走。2.3 Vue 前端项目构建前端打包之前检查一下环境变量文件。大部分 Vue 项目使用 Vite 构建环境配置在 .env.production 里。我见过一个非常典型的错误有人把后端接口地址直接写成了 http://localhost:8080/api本地开发验证没问题打包上传后手机访问页面所有接口请求全打到了手机自己的 localhost结果页面能开但数据全空。生产环境我推荐根本不去配置 VITE_API_BASE_URL而是让前端代码统一用相对路径 /api 开头。这样做的好处是代码部署在哪个域名下都不需要重新构建Nginx 负责把 /api 转发到正确的后端地址。如果你的项目里用到了各种二次封装过的 axios 实例把 baseURL 改成 /api然后重新 npm run build 生成前端产物。构建产物是 dist 目录里面是 index.html 和一堆静态资源。打包命令npm install npm run build如果 npm install 特别慢可以把 npm 源切换到国内镜像速度会提升非常多。构建完成后可以在本地用 nginx 或者随便一个静态服务器工具预览一下 dist 目录确认页面能正常打开、路由跳转没问题再上传到服务器。3. 后端部署实操数据库、配置与进程管理3.1 数据库准备与数据导入后端部署的第一步是先搞数据库。进入宝塔面板的数据库管理页面新建一个数据库名字建议和项目名一致或相近字符集选择 utf8mb4排序规则选 utf8mb4_general_ci 或 utf8mb4_unicode_ci 都行。utf8mb4 能存 emoji 表情和一些生僻字现在新项目基本都默认用它。数据导入有两种方式如果项目自带 SQL 初始化文件一般是 schema.sql、data.sql 这类在宝塔数据库管理页面点击“导入”选择 SQL 文件上传执行即可。如果项目用的是 Flyway 或 Liquibase 这种数据库版本管理工具启动后它会自动执行迁移脚本不需要手动导入。还有一种情况是项目原本在本地开发数据库里有测试数据想把本地数据同步到服务器可以用 Navicat 这类工具远程导出一份 SQL再到服务器上导入。注意导入前检查 SQL 文件里的建库语句如果包含 CREATE DATABASE执行时可能会因为库已存在而报错最稳妥的做法是把 SQL 文件里的 CREATE DATABASE 和 USE 语句去掉只留表结构和数据。数据库导入完成后创建一个专用的数据库账号也可以直接在面板里生成密码设置复杂一点。生产环境的密码千万别用 root 账号直接连业务库万一代码里把 SQL 注入漏洞放出来了拿到 root 权限就全完了。单独授权一个账号只给这个业务库的增删改查权限够用就好。3.2 后端配置文件调整与启动上传 jar 包到服务器后还要同步处理配置文件。我遇到过一个很折磨的场景项目代码是从某代码仓库拉下来后打包的本地调试和服务器上的数据库密码、Redis 密码都不一致但 jar 包里的配置文件已经被打包进去了改代码再重新打包很费时间。后来我的方案是在 jar 包同级目录放一个 application-prod.yml启动命令通过 --spring.config.location 参数指定外部配置文件这样改配置不需要动 jar 包。启动命令示例cd /www/wwwroot/project-name/backend nohup java -jar -Xms512m -Xmx1024m my-project.jar --spring.profiles.activeprod my-project.log 21 逐步解释一下-Xms512m -Xmx1024m 是设置 JVM 初始堆和最大堆内存根据你的服务器内存来调整别把最大堆设成超过服务器物理内存否则系统直接卡死nohup 是让进程在后台持续运行不随终端关闭而退出 my-project.log 21 是把标准输出和标准错误都写到日志文件里排查问题就靠这份日志--spring.profiles.activeprod 指定生产环境配置。启动后验证是否成功用下面几个命令# 查看进程是否存活 ps -ef | grep java # 查看端口是否监听 netstat -tlnp | grep 8080 # 实时跟踪日志输出 tail -f my-project.log如果日志里出现 Started Application in XX seconds 这类字样说明启动成功。要是日志报错比如数据库连接失败、端口被占用照着报错信息改配置重启即可。第一次启动时我用 tail -f 盯着日志能看到完整的启动过程比盲目等几秒钟再试接口要高效得多。3.3 使用进程守护管理器设置开机自启裸的 nohup 启动有一个问题进程会因为各种原因退出比如内存不够被系统杀掉、JVM 自身崩掉、服务器重启后进程消失。手动重新启动倒不难但生产环境不能总指望人守在那里。宝塔面板软件商店里的进程守护管理器配一个守护任务就行。配置要点运行目录填 jar 包所在目录启动命令填 java -jar my-project.jar --spring.profiles.activeprod日志文件填你希望输出日志的路径。勾选开机自启后重启服务器也能自动拉起进程。如果你用的系统里有 systemd也可以手写一个 service 文件效果一样但宝塔面板操作起来更直观对不熟悉 systemd 文件的开发者会更友好。进程守护管理器还有一个功能叫“异常退出自动重启”这个必须打开。Java 进程有时候因为突发情况挂掉守护进程检测到后会在几秒内重新拉起别人正在用的系统就不会受到长时间中断的影响。我实际使用中发现设置里跟内存相关的参数要谨慎调整别盲目把占用上限调大否则进程守护本身可能会和你的 Java 应用抢内存。4. 前端部署与跨域处理Nginx 站点配置详解4.1 上传前端文件与创建站点先把 vue 构建好的 dist 目录整体上传到服务器。路径我建议放在 /www/wwwroot/project-name/dist 下注意是 dist 目录里面的文件直接放在这个路径而不是 dist 目录再套一层。然后到宝塔面板的“网站”页面添加站点域名填你的实际域名如果还没有域名可以先填服务器 IP后面有域名了再修改。根目录指向 dist 目录其他选项默认。添加完站点之后默认配置基本是能访问静态页面的。但这里有一个关键点Vue 项目如果使用 history 路由模式直接访问非首页路由比如 /login、/user/profile会出现 404因为 Nginx 会去找磁盘上对应的物理路径而 SPA 里只有 index.html。解决办法是配置 try_files让所有非静态资源的请求都回退到 index.htmllocation / { root /www/wwwroot/project-name/dist; index index.html; try_files $uri $uri/ /index.html; }$uri 是请求的路径如果磁盘上找不到对应的文件就返回 $uri/ 目录下的 index通常也找不到最后回退到 /index.html。这样 Vue Router 拿到 index.html 后就能根据 URL 自己判断渲染哪个页面前端路由不会再 404。4.2 跨域问题的根因与 Nginx 反向代理配置前端能打开页面了但接口请求还是 404这是前后端分离项目部署时最普遍的问题。原因很简单Nginx 只拿到了静态文件不知道 /api 请求应该转给谁。我在 1.2 节梳理链路时说过Nginx 必须配置反向代理把 /api 开头的请求转发给 SpringBoot 进程。在 Nginx 配置文件里的 server 块中加一个 locationlocation /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_pass 后面的 URL 有没有带末尾斜杠。如果写成 proxy_pass http://127.0.0.1:8080;那么请求 /api/user/login 会原样转发到后端变为 /api/user/login如果写成 http://127.0.0.1:8080/则会把 /api/ 前缀去掉转发为 /user/login。这两种情况后端接口地址不同选哪种取决于你的后端 Controller 里有没有加 /api 前缀。我的项目里后端接口统一使用 /api 前缀所以 proxy_pass 不带末尾斜杠保证路径原样透传。这是最容易踩坑的地方一定要检查。另外三行 proxy_set_header 相信你已经见过很多次解释一下Host 保持浏览器发起请求时的域名Java 后端某些代码里用 request.getServerName() 获取当前域名来拼接绝对 URL 时没有这行拿到的就是 127.0.0.1:8080X-Real-IP 和 X-Forwarded-For 是为了让后端能记录真实客户端 IP不然日志里看到的全是 127.0.0.1。配置完成后每个 location 块都是独立的你需要注意 server 块里到底允不允许重叠匹配。实际操作中把静态文件的 location / 放在前面把 api 的 location /api/ 放在它后面Nginx 会按最长前缀匹配选择规则不会冲突。保存配置后执行 nginx -t 检查语法然后平滑重载nginx -t nginx -s reload重载完成后再访问页面打开浏览器开发者工具Network 面板里看接口请求的状态码如果请求从红色 404 变成 200说明反向代理通了如果变成 502大概率是后端进程没起来或者端口不对如果是 504可能是后端处理超时需要检查后端日志和数据库连接池配置。5. 安全组、域名与 HTTPS 配置5.1 云厂商安全组与端口放行很多新手在本地调试时一切正常一到服务器上死活访问不了最容易被忽略的就是云厂商的安全组。宝塔面板自带防火墙只是服务器内部的一层拦截而云厂商控制台的安全组相当于机房门口的保安两层都要放行才能放流量进来。以我这次部署为例我在云厂商控制台的安全组规则里放行了这些端口80 和 443Web 服务、SSH 端口用于远程连接。后端 8080 端口不要放行因为开放在公网上没有任何意义反而多一个攻击面。如果一开始没有域名只用 IP 测试只放行 80 也够前端通过 Nginx 转发到内网接口不需要公网直接访问 8080。如果你在服务器上用的是宝塔防火墙还要在宝塔安全页面里同样放行 80 和 443。两边都检查完了再访问域名测试如果还是打不开用本地 telnet 命令检查端口通不通比如 telnet 你的服务器IP 80。端口不通时优先查安全组和云平台那边的状态。5.2 域名解析到服务器有域名的话强烈建议直接绑域名比用 IP 访问体验好太多。进入你的域名服务商控制台添加一条 A 记录主机记录按需填 www 或 记录值填服务器 IPTTL 默认就好。解析生效一般几分钟到一小时内可以在本机用 ping 命令看域名是否解析到了新 IP。解析生效后回到宝塔的站点设置里把域名填进去一个站点可以绑定多个域名比如 example.com 和 www.example.com可以都绑到同一站点上。此时通过域名访问页面Nginx 会使用你配置的 server_name 匹配站点未来你甚至可以在同一台服务器上部署多个项目不同域名走不同的 server 块非常方便。记得把 4.2 节的 Nginx 配置放到域名对应的站点配置文件中而不是随便建一个.conf 文件。检查方式确认 /www/server/panel/vhost/nginx/ 里站点配置文件的 server_name 与你的域名匹配。可以每步都用 curl 测试curl -I http://你的域名返回 200 说明站点配置生效返回 404 则检查路径返回 403 则可能是目录权限或者 index 配置问题。5.3 申请 SSL 证书并启用 HTTPS现在的项目不上 HTTPS 基本说不过去。宝塔面板的 SSL 证书申请做得很省心在站点设置里打开 SSL 选项卡选择 Lets Encrypt勾选你绑定的域名点击申请即可。如果之前域名解析没有生效申请会失败所以先确保前面步骤完成了再走这一步。申请成功后宝塔会自动把证书文件放到对应目录并配置好站点。配置 HTTPS 后还需要设置强制跳转宝塔的 SSL 设置里直接有“强制 HTTPS”开关开启后访问 http 的请求会 301 跳到 https。这个很重要否则用户输入域名默认走 80 端口页面不够安全。部署完成之后在浏览器地址栏访问你的域名看到锁标志就说明证书生效了。顺便检查一下页面里有没有混入 http 请求的静态资源有的浏览器拦截混合内容可能导致某些图片或脚本加载不了。用浏览器开发者工具的 Console 看有没有 Mixed Content 报错如果报错去前端代码里把资源地址改成 https 或使用相对路径重新构建后部署。6. 部署完成后性能优化与常见故障排查6.1 常见问题速查表部署这件事百分之八十的时间都花在排错上。我把这些年遇到的高频问题整理成一张表你遇到类似情况可以直接对号入座。现象可能原因解决方案首页打不开一直转圈防火墙未放行 80/443Nginx 没启动域名解析未生效检查宝塔安全与云厂商安全组systemctl status nginxping 域名确认解析页面能开但接口 404反向代理 location 没配置proxy_pass 路径前缀不对检查 Nginx 配置文件 /api/ 段确认 proxy_pass 是否带斜杠接口 502 Bad Gateway后端 jar 包没启动成功后端端口与 proxy_pass 不一致检查 java 进程、netstat 端口、手动 curl localhost:8080 测试接口 504 Gateway Timeout后端接口执行慢数据库连接池或 SQL 慢查询看后端日志检查数据库连接池配置优化慢 SQL数据库连接失败数据库账号密码错误mysql 服务未启动端口未放行检查数据库配置systemctl status mysqld远程连一下数据库Vue 路由被访问时 404SPA 路由模式缺少 try_files 配置在 location / 中配置 try_files $uri $uri/ /index.html页面能开但图片加载不了静态资源路径写错磁盘权限不足检查 Nginx 静态路径与实际文件位置chmod 755 目录jar 包启动后自动退出端口被占内存不足配置文件加载错误检查 8080 端口占用看启动日志和进程守护日志调整 JVM 堆大小HTTPS 证书申请失败域名解析没生效80 端口不通确认解析和端口等待生效后重新申请修改 Nginx 配置后报错语法错误缺少分号或括号nginx -t 检查语法定位问题行号这张表不是万能药但确实能覆盖我遇到过的绝大部分问题。遇到报错时先别慌按“看日志 → 查端口 → 测链路”的顺序排查比盲目改配置高效十倍。举一个真实场景接口 502很多人直接去看后端代码其实第一步应该 curl http://127.0.0.1:8080 看后端是否在监听如果连接被拒绝说明进程没起来再去翻日志根本不用碰代码。6.2 内存、日志与日常维护注意事项Java 应用部署完不等于万事大吉运行期的维护更重要。内存这块如果你的服务器只有 2G 内存同时跑 MySQL、Nginx、Redis、SpringBoot很容易触顶。我建议给 JVM 堆设置上限比如 -Xmx512m让出一些空间给数据库和系统本身。同时把 MySQL 的性能配置调低一点宝塔面板里有 MySQL 性能调整选项选择小内存服务器模式避免 MySQL 占用过多内存导致 OOM。日志管理也要做好。nohup 启动方式下日志文件会越来越大几个月不管可能塞满磁盘。建议每周检查一下日志目录或者用 logrotate 做日志轮转。宝塔的后端项目日志也可以配在进程守护管理器里开启“写入运行日志”但依然存在日志膨胀问题。简单的做法是写一个 crontab 定时任务每天清理一下超过一定大小的日志文件或者按天分割日志。很多人会忽略磁盘空间直到某天站点打不开了SSH 进去一看磁盘 100%才发现是日志把磁盘塞满了。日常维护还少不了备份。宝塔的“计划任务”功能可以定时备份数据库到本地或云存储SQL 自动压缩后上传到其他地方更安全。我的习惯是每天凌晨备份一次数据库保留最近 7 天。代码本身有版本管理工具但数据库没有出问题时一份可用的备份比什么都值钱。6.3 先写到这里分享几个让我少走弯路的习惯部署完成之后再用一段时间会有一些自己的切身体会。我最后分享几个纯经验层面的东西都是实操后才理解到的希望能帮你少折腾几轮。第一严格分离环境配置。开发环境和生产环境的数据库、Redis、文件路径一定不要混用准备独立的 prod 配置文件从源头减少跨环境带来的低级故障。第二后端端口不要暴露公网。只要用了 Nginx 反向代理后端服务端口只需要监听 127.0.0.1加上安全组不放行双重保险。第三动手改 Nginx 配置前先备份原文件用 nginx -t 测过语法再 reload。改坏配置导致全员无法访问的教训经历过一次就足够长记性了。第四记录每次部署的操作过程和遇到的问题哪怕只是简单记在笔记软件里。一段时间后这些记录会成为你排障时最高效的参照。前后端分离项目的部署不难难的是一步一步把每个细节确认到位。按照我这套流程走一遍你会发现自己也能用一块干净的服务器面板把一个前后端分离项目从代码变成线上服务。实话说成功上线的感觉还挺有成就感的。下一次再部署类似项目流程熟练之后基本半小时左右就能完事熟练度和自动化程度会越来越高。