从本地跑通到公网访问:前后端项目部署实战
摘要:代码在本地跑得好好的,怎么让用户通过域名访问?本文从部署的底层认知出发,拆解服务器选购、安全组与防火墙配置、Nginx 反向代理、宝塔面板可视化管理,以一个全栈项目的完整部署流程为例,帮你打通从"本地跑通"到"线上可访问"的最后一公里。
📑 目录
- 部署是什么?------ 把菜从厨房端到餐厅
- 部署的本质:打包 → 上传 → 安装 → 启动
- 第一步:购买服务器与安全组配置
- 第二步:宝塔面板------运维的"图形化控制台"
- 第三步:域名与 DNS------让 IP 变成可读的网址
- 第四步:Nginx------流量入口的"总调度官"
- 第五步:部署前端项目
- 第六步:部署后端项目
- Nginx 动静分离与反向代理完整配置
- 互动讨论
部署是什么?------ 把菜从厨房端到餐厅
你写了一个全栈应用:前端是 React 做的精美界面,后端是 Express + MySQL 提供的 API。在本地,npm run dev 一敲,一切正常。
现在问题来了:怎么让全世界的人都能访问这个应用?
这就是部署(Deployment) 要解决的问题。部署的本质是:把运行所需的最小文件集放到服务器上,并在服务器上启动它们。
类比:你做好了一桌菜(开发完成),接下来要把菜从"厨房"(本地开发环境)端到"餐厅"(服务器),让客人(用户)吃。部署就是"端菜"的过程------怎么打包、怎么运输、怎么在服务器上把菜热好端上桌。
部署的本质:打包 → 上传 → 安装 → 启动
部署不是"把整个开发环境搬到服务器上"。标准流程只有四步:
text
本地打包 → 上传服务器 → 服务器上安装依赖/构建 → 启动服务
类比:搬家不是把整个房子搬过去,而是把家具打包、运到新家、在新家重新组装摆放。
| 步骤 | 前端(React) | 后端(Node.js) |
|---|---|---|
| 打包 | npm run build → dist/ |
npm run build → dist/ |
| 上传 | 通过 FTP/宝塔文件管理器上传 dist/ |
上传源码(或 dist/) |
| 安装 | 不需要(纯静态文件) | npm install --production |
| 启动 | Nginx 直接返回静态文件 | node dist/main.js |
第一步:购买服务器与安全组配置
购买服务器
你需要一台有公网 IP 的服务器。对于个人项目和小型应用,轻量云服务器(如腾讯云轻量应用服务器、阿里云 ECS)足够。
购买时需要选择:
- 操作系统:推荐 Ubuntu 或 CentOS(Linux 生态)
- 地域:选择离目标用户近的地域
- 配置:根据项目规模选择 CPU 和内存
安全组------云厂商的"小区大门保安"
服务器买好后,第一件事不是登录,而是配置安全组(Security Group) 。
误区 :以为在服务器上放行了端口就能被公网访问。
真相 :安全组和服务器防火墙必须同时放行,流量才能到达应用。
流量走向:
text
互联网 → 安全组(云平台) → 服务器网卡 → 服务器防火墙(OS) → 应用进程
安全组 (云平台控制台):在流量进入服务器的物理网卡之前拦截。默认全部拒绝,需手动放行端口。由云服务商提供。
| 端口 | 用途 | 建议 |
|---|---|---|
| 22 | SSH(远程登录) | 建议放行,但改为非默认端口(如 2222)增强安全 |
| 80 | HTTP | 必须放行 |
| 443 | HTTPS | 必须放行(如果配置 SSL) |
| 3000 | Node.js 后端 | 开发时可放行,生产环境建议关闭,走 Nginx 转发 |
| 3306 | MySQL | 绝对不放行(只允许 localhost 访问) |
黄金法则 :要让一个端口在公网被访问,必须在安全组 和服务器防火墙都放行。安全组优先级更高(先经过它)。
第二步:宝塔面板------运维的"图形化控制台"
什么是宝塔面板?
宝塔面板(BT Panel)是一个服务器运维管理面板,通过 Web 界面把 Linux 命令封装成"点按钮"操作,极大降低了运维门槛。
核心价值:
- 文件管理器 :替代
scp、mv、rm等命令,在浏览器里拖拽上传、移动、删除文件 - 软件商店:一键安装 Nginx、MySQL、Redis、PHP 等
- 网站管理:创建站点、配置反向代理、SSL 证书一键申请
- 终端:内置 SSH,不用另开客户端
安装宝塔面板
在服务器终端执行以下命令(根据操作系统选择):
bash
bash
# Ubuntu/Debian
wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh
# CentOS
yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh
安装完成后,会显示:
- 面板访问地址:
http://服务器IP:8888 - 用户名和密码
注意 :需要在安全组中放行 8888 端口才能访问面板。
服务器防火墙
宝塔面板安装后,会在服务器操作系统层面配置防火墙。需要在宝塔的安全菜单中放行所需端口(80、443、22 等)。
类比:安全组 = 小区大门口的保安亭(云厂商提供),服务器防火墙 = 你家房门上的锁(你自己装)。两个都要开,人才能进来。
第三步:域名与 DNS------让 IP 变成可读的网址
http://123.456.789.0 这样的 IP 地址难以记忆和传播。域名就是 IP 的"人类可读版本"。
DNS 解析流程
用户在浏览器输入 example.com 后:
- 浏览器缓存:检查本地是否缓存了该域名的 IP
- 操作系统缓存:检查系统 hosts 文件和 DNS 缓存
- 路由器/局域网 DNS:检查局域网 DNS 服务器缓存
- 网络运营商 DNS:递归查询
- 根 DNS 服务器 :逐级返回
.com→example.com的 IP
DNS 的本质是一种分布式数据库,根据域名逐级查找 IP 地址。
在宝塔中添加站点
在宝塔面板的网站 菜单中,点击添加站点:
- 输入域名(如
example.com) - 选择根目录(如
/www/wwwroot/example) - 选择是否创建 FTP 和数据库
第四步:Nginx------流量入口的"总调度官"
Nginx 是整个部署架构中的核心入口。它做三件事:
- 接受请求
- 返回静态文件(直接读磁盘)
- 转发动态请求(反向代理给后端)
生产端口策略:只开 Nginx 的端口
生产环境标准做法:只开放 Nginx 的 80/443 端口,后端服务(如 3000)、数据库(3306)全部不对外开放。
- 外部用户只能访问 Nginx
- Nginx 在服务器内部转发请求给后端
- 后端和数据库的攻击面大幅缩小
为什么 Nginx 处理静态文件这么快?
Nginx 处理静态文件时使用 sendfile 系统调用(零拷贝),数据从磁盘直接到网卡,不经过用户态内存拷贝。而 Node.js 处理静态文件时,数据需要从磁盘读到内存,再从内存发给 socket,多了一次拷贝。
所以 Nginx 处理静态文件比 Node.js 快得多,这也是为什么生产环境要把 Nginx 放在 Node.js 前面。
第五步:部署前端项目
在开始部署之前,先看看项目在本地运行时的样子。
此处插入图片:项目在本地运行的截图,显示"FUTURE CAPSULE"标题、输入框、作者、解锁时间设置和"SEAL CAPSULE"按钮,地址栏显示 localhost:5173
这是"未来胶囊"时间胶囊全栈应用的前端界面。用户写下给未来的自己的留言并设定解锁时间,到期前内容不可见。界面设计采用了瀑布流布局,支持无限滚动加载。
本地构建
在本地执行构建命令,生成静态文件:
bash
arduino
npm run build
# 产出 dist/ 或 build/ 目录
构建产物包含 .html、.css、.js 以及图片等静态资源。
此处插入图片:项目加载状态截图,显示"FUTURE_CAPSULE"标题和加载中的提示

上传到服务器
通过宝塔面板的文件管理器,将整个 dist/ 目录上传到服务器的网站根目录(如 /www/wwwroot/example.com/)。
配置 Nginx 站点
在宝塔面板的网站菜单中,创建或编辑站点配置:
nginx
ini
server {
listen 80;
server_name example.com;
root /www/wwwroot/example.com;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
try_files 的作用是支持前端路由(React Router 的 BrowserRouter):当访问 /about 这样的路径时,Nginx 会先检查是否存在该文件,如果不存在则返回 index.html,由 React 路由接管。
第六步:部署后端项目
方式一:服务器上构建(推荐)
将源码上传到服务器,在服务器上安装依赖并构建:
bash
bash
# 1. 上传源码到服务器(如 /www/wwwroot/api/)
# 2. 安装依赖
npm install --production
# 3. 构建(如果有 TypeScript)
npm run build
# 4. 启动服务
node dist/main.js
方式二:本地构建后上传
在本地构建好后,只上传 dist/ 目录和 package.json,服务器上只需安装生产依赖:
bash
bash
# 服务器上
npm install --production
node dist/main.js
使用 PM2 管理进程(推荐)
Node.js 应用需要持久运行 ,不能用 Ctrl+C 的终端方式。PM2 是 Node.js 进程管理器:
bash
bash
# 安装 PM2
npm install -g pm2
# 启动应用
pm2 start dist/main.js --name my-api
# 查看状态
pm2 status
# 开机自启
pm2 startup
pm2 save
在宝塔中配置反向代理
在宝塔面板的站点设置中,添加反向代理:
text
arduino
目标URL:http://localhost:3000
发送域名:localhost
等价于 Nginx 配置:
nginx
bash
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
这样,用户访问 http://example.com/api/users 时,请求被转发到 http://localhost:3000/api/users,由 Node.js 处理。
Nginx 动静分离与反向代理完整配置
将静态资源和动态 API 合并到一个配置文件中:
nginx
bash
server {
listen 80;
server_name example.com;
root /www/wwwroot/example.com;
index index.html;
# 静态资源:Nginx 直接返回
location /static/ {
alias /www/wwwroot/example.com/static/;
expires 7d;
}
# 前端路由:所有非 /api 的请求返回 index.html
location / {
try_files $uri $uri/ /index.html;
}
# 后端 API:转发给 Node.js
location /api/ {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
互动讨论
💬 部署的本质是什么?
部署的本质是"把运行所需的最小文件集放到服务器上,并在服务器上启动它们",而不是"把整个开发环境搬过去"。
💬 安全组和服务器防火墙有什么区别?
安全组在云平台的物理网卡前拦截,服务器防火墙在操作系统内核中拦截。两者必须同时放行端口,缺一不可。
💬 为什么生产环境只开 Nginx 的端口?
后端服务(如 3000)和数据库(如 3306)不对外开放,所有流量经过 Nginx 转发,减少攻击面。同时 Nginx 处理静态文件比 Node.js 快得多。
💬 前端路由(如 React Router)在 Nginx 上怎么配置?
使用 try_files $uri $uri/ /index.html:当访问 /about 等路径时,Nginx 先检查是否存在该文件,若不存在则返回 index.html,由前端路由接管。
💬 Node.js 应用怎么保证持久运行?
使用 PM2 进程管理器:pm2 start dist/main.js --name my-api。PM2 会在应用崩溃时自动重启,并支持开机自启。
💬 前后端分离项目的跨域问题在部署时怎么解决?
开发阶段用 Vite 代理或 CORS 插件;生产阶段用 Nginx 反向代理将 /api 请求转发给后端,前端请求同源的 /api 路径,不触发跨域。