最近把一个原本运行在微信小程序里的项目迁移成了 Web/PWA 版本,前端使用 Vue 3 + Vite。后端仍然运行在自己的服务器上,前端则希望找一个不需要额外维护 Nginx、支持 HTTPS、还能自动跟 GitHub 同步的平台。最后选择了 Cloudflare Pages。
实际用下来,这套流程非常适合个人项目:代码推到 GitHub,Cloudflare 自动拉取、构建、部署,之后每次更新 main 分支都可以自动发布。
整体结构
最终的部署结构其实很简单:
1 | GitHub Repository |
Cloudflare Pages 只负责前端静态资源,并没有把 FastAPI 后端一起搬过去。这一点比较重要。像 Vue、React、Vite 这类项目经过构建后,本质上都会得到一套 HTML、CSS 和 JavaScript 静态文件,因此非常适合放到 Pages 上。
先把前端放到 GitHub
项目本地目录大致如下:
1 | web/ |
先初始化 Git:
1 | git init |
然后在 GitHub 创建一个仓库,再添加远程地址:
1 | git remote add origin https://github.com/<username>/<repo>.git |
这里之前踩过一个很小的坑。一开始使用的是 SSH 地址:
1 | git@github.com:xxx/xxx.git |
但本地没有配置 GitHub SSH Key,因此出现:
1 | Permission denied (publickey) |
如果不打算专门配置 SSH,直接换成 HTTPS 地址即可:
1 | git remote set-url origin https://github.com/<username>/<repo>.git |
对于个人项目来说会省事很多。另外,下面这些文件不要提交:
1 | node_modules/ |
尤其是 .env,里面如果存在服务器配置或者密钥,不应该进入 GitHub。
在 Cloudflare Pages 连接 GitHub
进入 Cloudflare 控制台后创建 Pages 项目,选择:
1 | Workers & Pages |
授权 Cloudflare 访问 GitHub 后,就可以看到 GitHub 仓库。选择需要部署的仓库即可。这里有一个很容易弄混的地方:
Vue/Vite 静态网站应该创建 Cloudflare Pages 项目,不是直接使用 Workers 部署。
之前曾经尝试过:
1 | npx wrangler deploy |
结果走成了 Workers 的部署方式。虽然 Cloudflare Pages 和 Workers 现在在控制台里结合得越来越紧,但如果只是部署一个 Vue 静态网站,直接使用 Pages + Git Integration 会简单得多。
配置 Vue / Vite 构建参数
连接仓库后,Cloudflare 会要求填写构建配置。对于普通 Vue 3 + Vite 项目,可以这样配置:
1 | Production branch: |
如果 GitHub 仓库本身就是前端项目:
1 | repo/ |
那么:
1 | Root directory |
可以直接留空。
如果前端放在仓库里的子目录:
1 | repo/ |
那 Root directory 才需要填写:
1 | web |
Cloudflare 在部署时实际上做的事情和本地差不多:
1 | npm install |
然后把:
1 | dist/ |
部署到 CDN。
构建成功以后,会得到一个默认地址:
1 | https://项目名.pages.dev |
到这里,一个免费的 HTTPS 静态网站就已经上线了。
前端调用后端 API
静态网站通常还会调用自己的后端。例如:
1 | Vue |
Vite 项目一般使用环境变量:
1 | VITE_API_BASE_URL=https://<API域名> |
在代码中:
1 | const apiBaseUrl = import.meta.env.VITE_API_BASE_URL |
Cloudflare Pages 中可以在:
1 | Settings |
添加:
1 | VITE_API_BASE_URL |
值填写正式 API 地址。
VITE_*环境变量并不是服务器密钥。
Vite 会在:
1 | npm run build |
的时候直接把它写进浏览器最终下载的 JavaScript 中。
因此下面这种配置没问题:
1 | VITE_API_BASE_URL=https://api.example.com |
但绝对不能写:
1 | VITE_SECRET_KEY=xxx |
真正的 Secret 应该始终留在 FastAPI 后端。
修改环境变量后为什么没有生效?
这里也踩过一次坑。Cloudflare Pages 修改:
1 | VITE_API_BASE_URL |
以后,已经部署出去的网页不会自动变化。因为 Vite 的环境变量是在构建阶段写进去的。流程实际上是:
1 | VITE_API_BASE_URL |
所以修改环境变量后需要:
1 | Redeploy |
重新构建一次。否则网页仍然会使用上一次构建时的值。当时就出现过前端错误地请求:
1 | https://xxx.pages.dev/api/... |
而不是:
1 | https://<API域名>/api/... |
重新配置 VITE_API_BASE_URL 并部署后就恢复正常了。
后端还需要处理 CORS
前端和 API 不在同一个域名下:
1 | Frontend: |
对于浏览器来说,这是两个不同的 Origin。因此 FastAPI 需要允许 Pages 域名访问。
例如:
1 | from fastapi.middleware.cors import CORSMiddleware |
不太建议为了方便直接:
1 | allow_origins=["*"] |
正式环境最好明确允许自己的前端域名。如果还会使用 Cloudflare 的 Preview Deployment,例如:
1 | https://xxxxx.project.pages.dev |
也可以根据实际情况配置规则匹配 Preview 域名。
Vue Router 还需要 SPA 回退
Vue Router 使用 History 模式时,还有一个典型问题。例如直接访问:
1 | https://example.pages.dev/mine |
服务器实际上找不到:
1 | /mine |
这个 HTML 文件。因为真正存在的只有:
1 | index.html |
所以需要告诉 Cloudflare:
不管访问哪个前端路由,都先返回
index.html,然后交给 Vue Router 处理。
可以在:
1 | public/_redirects |
加入:
1 | /* /index.html 200 |
构建完成后:
1 | dist/_redirects |
也会存在。
这样:
1 | / |
这些路由直接刷新就不会出现 404。
GitHub 和 Cloudflare 最方便的地方:自动部署
完成 GitHub Integration 后,日常开发流程就非常简单了。
例如:
1 | git add . |
Cloudflare 会检测到 GitHub 更新:
1 | Git push |
不再需要:
1 | scp dist服务器 |
也不需要:
1 | 上传FTP |
更不用手动维护前端 Nginx。
对于一个个人项目来说,这一点体验很好。
Preview Deployment 也很实用
Cloudflare Pages 并不是只有正式环境。例如开发一个新功能:
1 | git switch -c feature/new-page |
Cloudflare 可以给这个分支创建一个独立 Preview 地址。这样可以先测试:
1 | feature branch |
比直接往正式网站上改安全很多。现在比较习惯的流程也是:
1 | feature/* |
最后再绑定自己的域名
默认的:
1 | xxx.pages.dev |
其实已经可以正常使用。如果希望网站看起来更正式,可以在 Pages:
1 | Custom domains |
添加:
1 | app.example.com |
如果 DNS 本身不托管在 Cloudflare,而是在其他 DNS 服务商,可以选择:
1 | My DNS provider |
然后按照 Cloudflare 提示添加 CNAME。通常类似:
1 | 类型:CNAME |
等待 DNS 生效后:
1 | https://app.example.com |
就会指向 Cloudflare Pages。HTTPS 证书也由 Cloudflare 自动处理。
这套部署方式适合什么项目
折腾完之后,会发现 Cloudflare Pages 并不是什么复杂的“云原生部署平台”,如果只是从个人开发角度理解,可以把它看成:
GitHub 仓库的自动构建 + 静态网站托管 + CDN + HTTPS。
对于:
Vue、React、Vite、个人博客、文档站、PWA、项目官网
都很合适。这次迁移之后,整个项目也变成了比较清晰的结构:
1 | GitHub |
前端更新交给 GitHub + Cloudflare,后端继续运行在自己的服务器上,两边互不影响。
相比把所有东西都部署在一台服务器上,这种方式最大的感受不是“性能提升了多少”,而是前端部署变得非常省心:提交代码之后,剩下的事情基本都由 Cloudflare 自动完成。
