前后端分离项目部署实战:从服务器配置到Nginx反向代理全流程

📅 2026/7/30 3:54:14
前后端分离项目部署实战:从服务器配置到Nginx反向代理全流程
1. 项目概述为什么前后端分离部署是“必选项”如果你是一名开发者最近在折腾一个自己的项目或者团队里正准备把老旧的单体应用拆开那你大概率绕不开“前后端分离”这个话题。这早就不是什么新鲜概念了但真到了要把它部署上线让用户能稳定访问的时候很多人还是会卡壳。我见过不少项目开发时前端用Vue、React写得飞起后端Spring Boot接口也调得顺畅结果一到部署环节不是跨域问题满天飞就是静态资源加载不出来再不就是Nginx配置写错导致接口404。最后只能草草把前端代码打包扔进后端项目的static目录里又回到了“伪分离”的老路。所以今天我们不聊为什么前后端分离好这已经是共识。我们直接切入最实际的环节如何把一个典型的前后端分离项目从你本地开发环境一步步、稳稳妥妥地部署到一台云服务器上让它能7x24小时对外服务。这整个过程就像给房子做精装修水电管线网络、代理、软装布置应用部署、安防监控进程守护一个都不能少。无论你用的是经典的“Spring Boot Vue”组合还是“Nest.js React”甚至是更新的技术栈其部署的核心思想和流程都是相通的。本教程的目标就是给你一份能“抄作业”的详细清单涵盖从服务器准备、环境安装、到应用部署、配置优化及问题排查的全链路让你避开我踩过的那些坑。2. 核心架构与部署方案选型在动手之前我们得先搞清楚我们要部署的东西到底是什么结构以及有哪些主流的部署方式。这决定了后续所有操作的路径。2.1 前后端分离项目典型结构解析一个标准的前后端分离项目在部署视角下可以清晰地看作两个独立的应用前端应用通常是一个单页应用SPA使用Vue、React、Angular等框架开发。它的最终产物是通过npm run build或yarn build生成的一堆静态文件index.html、JavaScript、CSS、图片字体等。这个应用没有独立的Web服务器开发时的dev-server只是为了调试它需要被托管在一个HTTP服务器如Nginx、Apache上由服务器把这些静态文件发送给用户的浏览器。后端应用提供RESTful API或GraphQL接口的服务端程序使用Spring Boot、Express、Django、Nest.js等框架开发。它的产物通常是一个可执行的JAR包Java、一组Node.js文件、或者一个Python应用。这个应用必须运行在一个应用服务器或运行时环境中比如Java需要JRENode.js需要Node环境Python需要Python解释器。它监听一个网络端口如8080等待前端发来的HTTP请求。关键关系用户浏览器只直接与前端托管服务器通信加载静态页面和脚本。前端脚本在浏览器中运行后再通过Ajax请求去调用后端API服务器的接口。这就引出了部署的核心问题如何让浏览器中的前端代码能正确找到并访问到运行在另一个地方的后端服务答案就是反向代理。2.2 主流部署方案对比与选型方案没有绝对的好坏只有适合与否。下面这张表对比了三种最常见的部署方式方案描述优点缺点适用场景传统服务器部署购买云服务器ECS手动安装Nginx、Java、Node.js、数据库等所有环境上传前后端代码并启动。1.完全可控深度定制化。2.成本透明按需配置服务器。3.学习曲线直接理解整个技术栈。1.运维繁琐需手动管理环境、更新、备份。2.环境一致性难保证“在我机器上好好的”。3.伸缩性差流量突增时手动扩容慢。中小型项目、学习实践、对成本敏感且有一定运维能力的团队。容器化部署使用Docker将前后端应用及其依赖打包成镜像通过Docker Compose或K8s编排部署。1.环境一致性极强镜像即环境。2.隔离性好应用互不干扰。3.易于CI/CD与流水线无缝集成。4.伸缩便捷。1.学习成本高需掌握Docker和编排工具。2.镜像管理需要额外仓库如Harbor。3. 轻微的性能开销。中大型项目、微服务架构、追求敏捷交付和DevOps的团队。Serverless/平台即服务前端部署到Vercel、Netlify后端部署到云函数AWS Lambda、阿里云FC、或PaaSHeroku、Railway。1.免运维专注业务代码。2.自动伸缩无需关心服务器。3.按量计费成本可能更低。1.厂商锁定Vendor Lock-in风险。2.冷启动问题可能导致延迟。3. 对本地资源、特定系统调用有限制。原型验证、轻量级API、事件驱动型应用、初创团队。我的选择与建议对于绝大多数初次尝试部署或个人项目我强烈推荐从传统服务器部署开始。理由很简单它能让你最透彻地理解从代码到服务的完整链条每一个环节都亲手摸过以后遇到问题你才知道从哪儿下手。容器化和Serverless是更高级的抽象它们解决的是传统部署的痛点但如果你连痛点都没亲身经历过直接上高级工具反而会云里雾里。本教程也将以一台全新的Linux云服务器为舞台演示传统部署的完整过程。掌握了这个再学Docker就是水到渠成。3. 服务器准备与基础环境搭建假设我们已经购买了一台Ubuntu 22.04 LTS的云服务器CentOS/RHEL系命令略有不同。拿到服务器的公网IP例如123.123.123.123和root密码后我们开始“装修”的第一步。3.1 初始服务器安全配置安全是第一步绝不能省略。直接用root用户操作是危险的。# 1. 使用SSH密钥登录比密码更安全 # 本地生成密钥对如果还没有: ssh-keygen -t rsa -b 4096 # 将公钥上传到服务器: ssh-copy-id root你的服务器IP # 2. 登录服务器 ssh root123.123.123.123 # 3. 创建新的普通用户例如 deploy adduser deploy # 按照提示设置密码和相关信息 # 4. 给新用户添加sudo权限 usermod -aG sudo deploy # 5. 切换到新用户后续操作都尽量在此用户下进行 su - deploy3.2 基础软件安装与配置我们将安装项目运行所必需的环境NginxWeb服务器/反向代理、后端运行时这里以Java和Node.js为例、数据库MySQL、以及版本控制工具Git。# 更新系统包列表 sudo apt update sudo apt upgrade -y # 安装Nginx sudo apt install nginx -y # 启动并设置开机自启 sudo systemctl start nginx sudo systemctl enable nginx # 此时访问 http://你的服务器IP应该能看到Nginx欢迎页 # 安装Java (以OpenJDK 17为例Spring Boot 3.x需要至少JDK 17) sudo apt install openjdk-17-jdk -y # 验证安装 java -version # 安装Node.js (通过NodeSource仓库安装LTS版本) curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt install -y nodejs # 验证安装 node --version npm --version # 安装MySQL sudo apt install mysql-server -y # 运行安全安装脚本设置root密码、移除匿名用户、禁止远程root登录等 sudo mysql_secure_installation # 登录MySQL为你的项目创建数据库和用户 sudo mysql # 在MySQL提示符下执行 # CREATE DATABASE your_project_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # CREATE USER your_project_userlocalhost IDENTIFIED BY StrongPassword123!; # GRANT ALL PRIVILEGES ON your_project_db.* TO your_project_userlocalhost; # FLUSH PRIVILEGES; # EXIT; # 安装Git sudo apt install git -y注意生产环境的MySQL配置远不止这些还需要考虑调整innodb_buffer_pool_size、连接数、二进制日志等。这里只是保证服务能跑起来。务必使用强密码且不要使用root用户直接连接应用。4. 前端项目部署详解前端项目部署的核心是将构建好的静态文件放到Nginx能够服务的目录下并正确配置Nginx。4.1 本地构建与文件上传首先在你的本地开发机完成前端项目的构建。# 进入你的前端项目根目录 cd /path/to/your-frontend-project # 安装依赖如果node_modules不存在 npm install # 执行构建生成dist或build文件夹 npm run build构建成功后项目根目录下会生成一个dist文件夹Vue CLI默认或build文件夹Create React App默认里面就是我们要部署的静态资源。接下来将dist文件夹上传到服务器。有多种方式使用scp命令简单直接# 从本地上传整个dist目录到服务器的/home/deploy目录下 scp -r ./dist deploy123.123.123.123:/home/deploy/使用Git适合自动化在服务器上克隆仓库然后执行构建。但需要服务器有Node环境且构建可能消耗资源。使用CI/CD工具如Jenkins, GitHub Actions这是更专业的做法自动化构建、测试、部署。我们先用scp简单明了。4.2 Nginx配置与反向代理设置这是前端部署最关键的一步。我们需要配置Nginx让它托管我们上传的静态文件。将API请求转发到后端服务器。# 在服务器上将上传的dist文件夹移动到Nginx的默认站点目录 sudo mv /home/deploy/dist /var/www/html/your-frontend-app # 创建一个新的Nginx配置文件 sudo vim /etc/nginx/sites-available/your-project将以下配置写入文件。请仔细阅读注释理解每一行的作用server { listen 80; server_name your-domain.com www.your-domain.com; # 如果没有域名可以用服务器IP但更推荐用IP访问后端前端用域名或IP均可 root /var/www/html/your-frontend-app; # 静态文件根目录 index index.html; # 开启gzip压缩提升传输效率 gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss text/javascript; # 核心配置处理前端路由如Vue Router的history模式 # 当请求的不是一个真实存在的文件或目录时将请求重写到index.html由前端路由接管 location / { try_files $uri $uri/ /index.html; } # 反向代理配置将所有以 /api/ 开头的请求转发到后端应用 location /api/ { # 后端应用运行在localhost的8080端口 proxy_pass http://localhost: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; } # 可选代理WebSocket连接如果你的应用用了WebSocket # location /ws/ { # proxy_pass http://localhost:8080; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection upgrade; # } }关键点解析try_files $uri $uri/ /index.html;这是支持SPA前端路由history模式的灵魂语句。没有它你刷新非首页的页面就会得到Nginx的404错误。location /api/这里定义了反向代理的规则。所有前端发往/api/users、/api/login的请求都会被Nginx透明地转发到http://localhost:8080/api/users和http://localhost:8080/api/login。这样前端代码里的API基地址直接写成/api即可完美解决跨域问题因为对于浏览器来说所有请求都是发给同一个域名/端口的。proxy_set_header这几行至关重要它们将用户的真实IP、协议等信息传递给后端否则后端日志里看到的客户端IP可能全是127.0.0.1。启用站点配置并测试# 创建符号链接启用该站点 sudo ln -s /etc/nginx/sites-available/your-project /etc/nginx/sites-enabled/ # 测试Nginx配置语法是否正确 sudo nginx -t # 如果显示 syntax is ok 和 test is successful则重启Nginx sudo systemctl reload nginx现在访问你的服务器IP或域名应该能看到前端页面了。但点击任何需要调用后端接口的按钮都会失败因为后端服务还没启动。5. 后端项目部署详解后端部署的核心是让应用作为一个常驻进程运行起来并确保它崩溃后能自动重启。5.1 应用打包与上传以Spring Boot为例在本地使用Maven或Gradle打包。# 在本地后端项目根目录 ./mvnw clean package -DskipTests # 或者使用Gradle ./gradlew bootJar打包后在target目录下会生成一个可执行的JAR文件例如your-backend-app-0.0.1-SNAPSHOT.jar。将这个JAR文件上传到服务器scp ./target/your-backend-app-0.0.1-SNAPSHOT.jar deploy123.123.123.123:/home/deploy/app/在服务器上建议为应用创建一个专属目录比如/home/deploy/app并将JAR包、配置文件、日志目录都放在这里方便管理。5.2 使用Systemd守护进程最可靠的方式是使用Systemd来管理你的Java应用。它提供开机自启、自动重启、日志集成等功能。# 在服务器上创建Systemd服务单元文件 sudo vim /etc/systemd/system/your-backend.service写入以下内容根据你的实际情况修改[Unit] DescriptionYour Backend Application Service Afternetwork.target mysql.service # 表示在网络和MySQL服务启动后再启动本服务 [Service] Typesimple Userdeploy # 使用非root用户运行更安全 WorkingDirectory/home/deploy/app # JAR包所在的目录 ExecStart/usr/bin/java -Xms256m -Xmx512m -jar your-backend-app-0.0.1-SNAPSHOT.jar # 重要参数说明 # -Xms256m 初始堆内存 # -Xmx512m 最大堆内存 # -jar 后面是你的JAR包名 # 如果你的应用有额外的配置文件可以使用 --spring.config.locationfile:/path/to/application-prod.yml SuccessExitStatus143 # Spring Boot应用在收到终止信号时通常返回143 Restartalways # 总是重启 RestartSec10 # 重启前等待10秒 StandardOutputjournal # 输出到系统日志 StandardErrorjournal [Install] WantedBymulti-user.target实操心得Userdeploy这一行非常重要。永远不要用root用户直接运行你的应用。这能最大程度限制应用被入侵后的影响范围。同时确保/home/deploy/app目录的所属权和权限正确chown -R deploy:deploy /home/deploy/app。启动并启用服务# 重新加载Systemd配置 sudo systemctl daemon-reload # 启动服务 sudo systemctl start your-backend # 设置开机自启 sudo systemctl enable your-backend # 查看服务状态和日志 sudo systemctl status your-backend sudo journalctl -u your-backend -f # 实时查看日志如果状态显示active (running)并且日志中没有明显的错误那么后端API应该已经在localhost:8080运行起来了。5.3 后端配置文件以Spring Boot为例你的应用在本地开发时可能用application.yml部署时需要区分环境。通常我们会准备一个application-prod.yml。# application-prod.yml server: port: 8080 address: 127.0.0.1 # 只监听本地回环地址通过Nginx反向代理访问更安全 spring: datasource: url: jdbc:mysql://localhost:3306/your_project_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: your_project_user password: StrongPassword123! driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: validate # 生产环境绝对不要用 update 或 create用validate或none。 show-sql: false # 生产环境关闭SQL日志 # 自定义配置如文件上传路径 file: upload-dir: /home/deploy/app/uploads # 日志配置 logging: file: name: /home/deploy/app/logs/app.log level: com.yourcompany: INFO将这个配置文件也上传到服务器的/home/deploy/app目录并在Systemd服务的ExecStart命令中通过--spring.config.location指定它。6. 全链路联调与测试现在前端Nginx:80、后端Java:8080、数据库MySQL:3306都已就位。是时候进行端到端的测试了。测试前端静态资源直接访问http://你的服务器IP确保页面加载正常CSS、JS、图片等资源没有404错误。测试后端API直接访问在服务器本地用curl测试后端是否正常响应。curl http://localhost:8080/api/health如果返回预期的JSON数据或状态码说明后端独立运行正常。测试Nginx反向代理这是最关键的一步。访问http://你的服务器IP/api/health。这个请求应该被Nginx接收到并转发给后端的localhost:8080/api/health最后将响应返回给浏览器或curl。如果这里出错最常见的是502 Bad Gateway或504 Gateway Timeout问题通常出在Nginx配置proxy_pass地址不对或后端服务没启动。测试前端调用后端在前端页面进行登录、查询等操作打开浏览器的开发者工具F12的“网络(Network)”标签页查看发起的API请求状态码是否为200响应数据是否正确。检查跨域问题理论上通过Nginx反向代理前端请求/api就是请求自己的域名不存在跨域。如果你在控制台看到CORS错误请检查Nginx配置中location /api/的proxy_pass是否正确。后端代码中是否错误地配置了CORS在生产环境下如果用了反向代理后端通常可以移除CORS配置因为请求来源变成了Nginx是同源的。或者将CORS配置为允许Nginx的域名/IP。7. 部署进阶安全、性能与监控基础部署完成只是第一步要让服务稳定可靠还需要做更多工作。7.1 配置HTTPS使用Let‘s Encrypt免费SSL证书HTTP是明文的极不安全。我们必须上HTTPS。# 安装Certbot客户端和Nginx插件 sudo apt install certbot python3-certbot-nginx -y # 获取并安装证书假设你的域名DNS已解析到该服务器 sudo certbot --nginx -d your-domain.com -d www.your-domain.comCertbot会自动修改你的Nginx配置将HTTP重定向到HTTPS并配置好SSL证书。证书每90天会自动续期。7.2 Nginx性能与安全调优在/etc/nginx/nginx.conf的http块中可以添加一些全局优化http { # 隐藏Nginx版本号增加安全性 server_tokens off; # 客户端请求体最大大小防止过大请求攻击 client_max_body_size 10m; # 优化文件传输 sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; # 限制连接频率防CC攻击需在对应的server或location中配置 # limit_req_zone $binary_remote_addr zoneone:10m rate10r/s; # limit_req zoneone burst20 nodelay; # 包含站点配置 include /etc/nginx/sites-enabled/*; }7.3 应用进程监控与日志管理监控Systemd服务使用systemctl status your-backend定期检查服务状态。可以配置简单的监控脚本当服务挂掉时发送告警通过邮件、钉钉、企业微信等。日志轮转应用日志/home/deploy/app/logs/app.log会不断增长需要配置logrotate。sudo vim /etc/logrotate.d/your-backend-app内容如下/home/deploy/app/logs/*.log { daily missingok rotate 30 # 保留30天的日志 compress delaycompress notifempty create 0640 deploy deploy sharedscripts postrotate systemctl reload your-backend /dev/null 21 || true endscript }使用Supervisor备选如果你部署的是Python、Node.js等非Java应用Systemd配置可能稍复杂。Supervisor是一个更通用的进程管理工具配置起来也很直观。8. 常见问题与故障排查实录部署路上坑无数这里记录几个我踩过且高频出现的问题。8.1 前端页面刷新404SPA路由问题现象首页能打开但点击内部链接或手动刷新非首页的URL时显示Nginx 404页面。原因Nginx把/about这样的路径当成了一个实际的文件或目录请求去查找当然找不到。解决确保Nginx配置中处理根路径的location /块里包含了try_files $uri $uri/ /index.html;这条指令。Vue Router的history模式和React Router的BrowserRouter都依赖这个。8.2 502 Bad Gateway / 504 Gateway Timeout现象浏览器访问/api接口返回502或504错误。排查步骤检查后端服务是否运行sudo systemctl status your-backend。如果没运行查看日志journalctl -u your-backend找原因。检查Nginx配置确认proxy_pass http://localhost:8080;中的端口号与后端应用实际监听的端口一致。检查网络连通性在服务器上执行curl http://localhost:8080/api/health看后端本身是否正常响应。如果不通问题在后端。检查防火墙Ubuntu默认的ufw防火墙可能阻止了端口。确保后端端口如8080允许本地访问或者直接暂时关闭防火墙测试sudo ufw disable测试完记得开启并配置规则。504超时可能是后端处理请求太慢。可以适当增加Nginx的代理超时时间location /api/ { proxy_pass http://localhost:8080; proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 120s; # 根据你的接口最大耗时调整 # ... 其他header设置 }8.3 静态资源CSS/JS/图片加载失败现象页面能打开但样式错乱控制台报资源404。排查检查Nginx配置中的root指令路径是否正确指向了包含index.html的构建输出目录如/var/www/html/your-frontend-app。检查构建产物的路径。有些前端项目构建后资源文件可能带有哈希值并放在static或assets子目录下。确保Nginx的root配置指向的是包含这些子目录的父级目录。在前端项目的构建配置中如Vue的vue.config.js或React的package.json中的homepage字段检查publicPath设置。如果部署在非根路径如/admin/需要在这里和Nginx的root配置中做相应调整。8.4 数据库连接失败现象后端启动失败日志显示Access denied for user或Cannot create connection to database server。排查确认数据库服务运行sudo systemctl status mysql。检查连接参数确认application-prod.yml中的数据库URL、用户名、密码完全正确。特别注意密码中的特殊字符是否需要转义。检查用户权限登录MySQL确认你创建的用户和数据库存在且用户对该数据库有全部权限并且允许从localhost连接。检查MySQL绑定地址生产环境MySQL默认只监听127.0.0.1这是对的。确保你的连接字符串里也是localhost或127.0.0.1而不是服务器的公网IP。部署是一个系统工程一次成功可能意味着你运气好但更常见的是需要反复调试。我的建议是分段验证日志为王。每完成一步就立刻验证这一步是否成功并善用journalctl、Nginx的error.log/var/log/nginx/error.log和浏览器开发者工具它们能提供绝大部分问题的线索。当你亲手把第一个前后端分离项目部署上线并稳定运行起来后你对整个Web应用架构的理解会深刻得多。