我把"时间胶囊"部署上腾讯云宝塔,跨域从根源消失了

我把"时间胶囊"部署上腾讯云宝塔,跨域从根源消失了

我做了个叫 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 剥掉前缀转发。如果后端同时有 userorder 多个服务在不同端口,nginx 该怎么配合 location 做按路径路由?评论区聊聊你的方案。


相关推荐
东风破_9 小时前
danci 2:创建的单词书到底存在哪里?从 Supabase 一路理解 ORM、Drizzle 和 RLS
数据库·后端·node.js
东风破_10 小时前
danci 项目(三):从 JSON 数据到 AI Coding,真实项目里的数据清洗、Prompt 和工程规范
前端·后端·node.js
东风破_10 小时前
danci 项目(一):从需求到架构,一个单词学习系统为什么会这样设计
前端·后端·node.js
__zRainy__16 小时前
Node系列 · Express:常用的中间件
中间件·node.js·express
时寒的笔记18 小时前
jsvmp04_某yuan宝日志还原rc4
node.js
coderCN18 小时前
Nodejs 第三十四章 数据库(表达式和函数、子查询和连表)
前端·node.js
5233AI开发笔记1 天前
Node.js 调用 API:为什么秘密型 Key 不能放前端
node.js
2601_962076251 天前
Node.js 完全教程:从入门到精通
node.js
超级码里奥20241 天前
OpenClaw(龙虾[特殊字符])AI 智能体安装教程
windows·node.js