我把"时间胶囊"部署上腾讯云宝塔,跨域从根源消失了
我做了个叫 FUTURE_CAPSULE(时间胶囊) 的全栈小应用:可以给未来的自己留一段话、设一个解锁时间,倒计时结束前它被锁着显示 [ LOCKED ],解锁后才长出文字。前端是 React 的瀑布流页面,后端是 Node + express + MySQL。
本地开发一切正常。但我有一套前端 localhost:5173,一套后端 localhost:3001,于是逃不掉那个经典报错:
text
from origin 'http://localhost:5173'
has been blocked by CORS policy
又是跨域。我一度以为"前后端分离 = 必须和 CORS 搏斗"。直到我把它部署到一台腾讯云轻量服务器上用宝塔面板跑起来,发现:
部署完之后,跨域问题消失了。我一行 CORS 代码都没改,甚至没写。 只是换了个入口。
这篇文章就用这个真实的"时间胶囊"项目,讲清楚那个反直觉的真相:Nginx 反向代理把前后端收进同一个源,跨域从根本前提上就不成立了。
为什么本地会跨域:两个源
跨域不是后端的问题,是浏览器的同源策略拦下来的------协议、域名、端口三者必须完全一致才算"同源"。
本地开发时我们有两个源:
text
前端(浏览器访问) http://localhost:5173 ← vite 开发服务器
后端(数据接口) http://localhost:3001 ← node 接口
↑ 仅端口不同 = 不同源
端口不同就是不同源,浏览器直接拦截。所以本地代码里我不得不开 CORS 兜底:
ts
// server/src/app.ts ------ 本地开发我开了 CORS
app.use(cors());
但注意,这个 cors() 是针对"两个源"续命用的。部署之后,根本不该再靠它------这正是很多人绕不过去的误区。
部署后:Nginx 成了唯一入口
npm run build 把前端 React 打成了 dist/ 静态文件;后端 node 跑着接口。但生产环境没有 vite 这层代理了。谁管请求该去哪?
Nginx。 它一个端口,统管前后端:
text
用户 → http://1.13.20.252 (只这一个入口,只开 80)
│
┌───────┴────────┐
│ Nginx │
│ │
┌────┴─────┐ ┌─────┴────┐
│ 静态请求 │ │ 动态请求 │
│ / → dist/ │ │ /api → node│
│ (前端页面) │ │ (3001) │
└──────────┘ └──────────┘
关键 :用户从头到尾只打 http://1.13.20.252 这一个源------
text
前端页面 html http://1.13.20.252/
请求数据 /api http://1.13.20.252/api/capsules
↑ 同一个协议 + 同一个域名 + 同一个端口
端口相同 = 同源 。跨域的前提没了,于是 cors() 变成了死代码。
这就是最新颖的地方:我这个项目的客户端
.env里直接写VITE_API_URL=http://1.13.20.252(同源),根本不用填后端子域名;服务器里的app.use(cors())在生产环境纯属冗余------nginx 已经把源统一了。
Nginx 配置:其实就两段 location
腾讯云宝塔里,我为这个站点做的 nginx 配置,核心就两个 location:
nginx
server {
listen 80;
server_name 1.13.20.252;
# 1. 静态请求:直接把前端打包产物吐给浏览器
location / {
root /www/wwwroot/future_capsule/dist;
index index.html;
try_files $uri $uri/ /index.html; # 🔑 前端路由回退,别漏
}
# 2. 动态请求:/api 反代给 node 后端
location /api/ {
proxy_pass http://127.0.0.1:3001/; # 🔑 末尾斜杠,剥掉 /api 前缀
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
拆开看 Nginx 的"三件事":
| Nginx 干的活 | 对应配置 | 时间胶囊里的作用 |
|---|---|---|
| 接受请求 | listen 80 |
对外唯一入口 |
| 发静态文件 | location / → root .../dist |
把前端瀑布流页面返回给浏览器 |
| 转发给后端 | location /api/ → proxy_pass |
把 /api/capsules 转给 node 3001 |
一句话记忆:静态的走 Nginx 本地文件,动态的走 Nginx 转发给 node。Nginx 一分为二,前端后端各取所需,且全程同源。
反代时最容易踩的坑:/api 前缀剥不剥
宝塔默认给的 proxy_pass 很可能不带末尾斜杠。它的行为差异,正是新手翻车点:
nginx
# ❌ 不带斜杠:转发时保留 /api 前缀
proxy_pass http://127.0.0.1:3001;
# → node 收到 /api/capsules(后端路由是 /capsules,404)
# ✅ 带斜杠:剥掉 /api 前缀
proxy_pass http://127.0.0.1:3001/;
# → node 收到 /capsules,正好命中
我的后端路由是 /api/capsules 下挂着 /(见 routes/capsule.ts):浏览器打 /api/capsules,nginx 剥掉 /api 后转给 node 的 /capsules,配得正正好。
⚠️ 坑 :
proxy_pass结尾有没有/,决定 nginx 是否剥掉location匹配到的前缀。前后端部署时先想清楚你后端路由带不带/api,再决定斜杠要不要。
Nginx 在整条访问链里站在哪
放回"用户敲网址到拿到数据"的完整链路,Nginx 只负责其中一环,但它是关键的一环:
text
浏览器输入 http://1.13.20.252
│ 1. DNS:把域名解析成公网 IP
▼
腾讯云安全组 → 只放行 80 / 443 ← 云厂商网络层"大门保安"
│
▼
宝塔/系统防火墙 → 同样只放行必要端口 ← 服务器内部"楼栋门禁"
│
▼
Nginx(80 端口) ← "前台接待",真正的入口
├─ 静态请求 → dist/ 文件
└─ /api 请求 → 反代 → node 3001
↓
node → MySQL 「重型以太」
| 环节 | 位置 | 类比 |
|---|---|---|
| DNS | 全球分布式 | 查电话簿找地址 |
| 安全组 | 腾讯云网络层 | 小区大门保安 |
| 防火墙 | 服务器系统内部 | 楼栋门禁 |
| Nginx | 服务器 80 端口 | 前台接待,分流找人 |
记住这条链路的价值:出问题时你知道去查哪一环。
/api接口 502,先看 node 逻辑对不对、再看 nginx 反代地址;页面 404,先看dist/有没有传上去、路由回退写没写。分清了层次,排查不再瞎试。
用腾讯云宝塔,部署变成"点按钮"
命令行部署 Linux 对新手劝退(装 nginx、配置、传文件、改权限、看日志,每步都可能卡)。我用的腾讯云轻量服务器 + 宝塔面板(BT Panel),把命令行操作变成了可视化点击:
text
纯命令行玩法:ssh 登录 → 各种命令行 → 很累
宝塔玩法:网页后台点几下 → 完成
我在宝塔网页后台做的事:
| 目的 | 宝塔里的动作 |
|---|---|
| 装 Node.js、nvm 多版本 | 「软件商店」装 Node.js 版本管理器,指针切版本 |
| 装 Nginx | 「软件商店」装 nginx |
| 建数据库 | 「数据库」新建 future_capsule 库(我分了 dev/production 两套,开发线上互不影响) |
| 放代码 | /www/wwwroot/future_capsule 上传 dist + server |
| 配反代、配 HTTPS | 可视化编辑站点 nginx 配置、一键部署 SSL 证书 |
而后端要接 MySQL,就在 .env 里配置宝塔建好的连接信息:
env
# server/.env
DB_HOST=你的宝塔MySQL主机IP
DB_PORT=3306
DB_USER=future_capsule
DB_PASSWORD=你的密码
DB_NAME=future_capsule
PORT=3001
建议:自己玩 / 中小项目,宝塔面板上手最快,能把精力放在"部署逻辑"而不是 Linux 命令。规模大了再回命令行也不迟。
结尾
那句让我愣住的观察,现在你明白了:
跨域不是我"解决"的,是我让它"不发生"的。 本地用 vite 代理或 CORS,是因为有 5173 和 3001 两个源;生产用 nginx 反代,是把前后端统一进同一个源。
一句话收尾:
本地开发用 proxy / CORS,是因为有两个源;生产部署用 Nginx 反向代理,是把前后端收进同一个源------源统一了,跨域就根本不存在。
下次再有人说"前后端分离必然跨域",你可以接一句:那是你没在它们前面放一个 Nginx。
留个开放问题:我这套 proxy_pass http://127.0.0.1:3001/ 是把 /api 剥掉前缀转发。如果后端同时有 user、order 多个服务在不同端口,nginx 该怎么配合 location 做按路径路由?评论区聊聊你的方案。