从 VPS 到 Cloudflare Pages:我的静态网站自动部署流程

从 VPS 到 Cloudflare Pages:我的静态网站自动部署流程记录

这篇文章记录一下我把一个 React/Vite 静态工具站,从传统 VPS + 1Panel 部署,迁移到 GitHub + Cloudflare Pages 自动部署的完整过程。

最终实现的效果是:

本地修改代码

推送到 GitHub

Cloudflare Pages 自动构建

自动发布到 51qdd.com

以后更新网站,只需要执行:

git add .
git commit -m "更新内容"
git push

Cloudflare 就会自动完成构建和发布,不再需要手动上传 dist 文件,也不需要登录 1Panel 修改 Nginx 或重载 OpenResty。


一、原来的部署方式

最开始,我的网站是部署在香港 VPS 上的,使用的是 1Panel 面板。

整体结构大概是:

用户

Cloudflare

香港 VPS

1Panel

OpenResty / Nginx

静态网站目录

域名最开始是:

finance.51qdd.com

对应的服务器目录类似:

/www/sites/finance.51qdd.com/index

每次网站更新时,需要手动执行:

npm run build

然后把生成出来的静态文件上传到 1Panel 的网站目录。

例如 Vite 项目构建后会生成:

dist/
├── assets/
├── favicon.svg
└── index.html

上传到服务器后,目录应该类似:

/www/sites/finance.51qdd.com/index/
├── assets/
├── favicon.svg
└── index.html

这种方式能用,但是维护起来比较麻烦。

每次改代码都要:

本地构建

找到 dist 文件

上传到 1Panel

覆盖旧文件

如果路由有问题,还要改 Nginx

对于一个纯静态网站来说,这种方式有点重。


二、项目技术栈

我的项目是一个理财计算工具站,主要用于存款收益、贷款月供、复利收益、日期规划等计算。

技术栈是:

React
Vite
Tailwind CSS
Cloudflare Pages
GitHub

项目目录大致如下:

fiance/
├── apps/
│   └── web/
│       ├── public/
│       ├── src/
│       ├── package.json
│       ├── vite.config.js
│       └── index.html
├── package.json
├── package-lock.json
└── README.md

其中真正的网站代码在:

apps/web

三、本地运行项目

进入项目目录:

cd apps/web

安装依赖:

npm install

启动开发服务:

npm run dev

Vite 默认会启动一个本地开发地址,通常是:

http://localhost:5173

如果想停止本地开发服务,在终端里按:

Ctrl + C

即可结束。


四、本地构建静态文件

在项目目录执行:

npm run build

构建成功后,会生成:

dist/
├── assets/
├── favicon.svg
└── index.html

这个 dist 目录就是最终可以部署到静态托管平台的文件。

如果是部署到 1Panel,只需要上传 dist 里面的内容即可。

注意,不是上传整个 dist 文件夹,而是上传里面的内容。

正确结构:

网站根目录/
├── assets/
├── favicon.svg
└── index.html

错误结构:

网站根目录/
└── dist/
    ├── assets/
    ├── favicon.svg
    └── index.html

五、VPS + Nginx 部署时遇到的问题

因为 React/Vite 项目通常是单页应用,直接访问首页没有问题,但是刷新子页面时可能会出现 404。

例如:

https://finance.51qdd.com/tools/deposit-yield

第一次从首页点击进入正常,但如果直接刷新页面,就会显示:

404 Not Found
openresty

原因是服务器会去查找真实路径:

/tools/deposit-yield

但是静态目录里并没有这个文件。

真实存在的只有:

index.html
assets/

所以需要让所有路由都回退到 index.html

Nginx 配置需要加入:

location / {
    try_files $uri $uri/ /index.html;
}

完整示例:

server {
    listen 80;
    listen 443 ssl http2;
    server_name finance.51qdd.com;

    root /www/sites/finance.51qdd.com/index;
    index index.html index.htm;

    access_log /www/sites/finance.51qdd.com/log/access.log main;
    error_log /www/sites/finance.51qdd.com/log/error.log;

    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    location ^~ /.well-known/acme-challenge {
        allow all;
        root /usr/share/nginx/html;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }

    error_page 404 /index.html;

    ssl_certificate /www/sites/finance.51qdd.com/ssl/fullchain.pem;
    ssl_certificate_key /www/sites/finance.51qdd.com/ssl/privkey.pem;
    ssl_protocols TLSv1.3 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    error_page 497 https://$host$request_uri;

    add_header Strict-Transport-Security "max-age=31536000";
}

这个配置可以解决 React 子路由刷新 404 的问题。

但是后来我发现,对于纯静态网站来说,其实完全没有必要继续依赖 VPS。


六、为什么迁移到 Cloudflare Pages

我的网站是纯静态网站:

无数据库
无登录系统
无后端接口
无文件上传
所有计算都在浏览器本地完成

这种网站非常适合托管到 Cloudflare Pages。

迁移后结构变成:

用户

Cloudflare 边缘节点

Cloudflare Pages

静态文件

相比 VPS 部署,Cloudflare Pages 有几个优势:

不需要服务器
不需要维护 Nginx
不需要手动申请 SSL
不需要手动上传文件
支持 GitHub 自动部署
全球 CDN 加速
免费额度足够个人网站使用

对于个人博客、工具站、文档站来说,非常合适。


七、把代码上传到 GitHub

如果项目还没有初始化 Git,可以先执行:

git init

添加所有文件:

git add .

提交:

git commit -m "Initial commit"

关联 GitHub 仓库:

git remote add origin https://github.com/Crazy1wh/fiance.git

把分支改成 main

git branch -M main

推送到 GitHub:

git push -u origin main

后续每次更新,只需要:

git add .
git commit -m "更新网站内容"
git push

八、创建 Cloudflare Pages 项目

进入 Cloudflare 后台:

Workers & Pages

Create application

Pages

Connect to Git

然后选择 GitHub 账号和仓库。

例如:

GitHub account: Crazy1wh
Repository: fiance

点击:

Begin setup

九、Cloudflare Pages 构建配置

因为我的项目是 React + Vite,所以 Framework Preset 选择:

React static

如果你的 Cloudflare 后台有 Vite,也可以选择 Vite。

我的项目代码在:

apps/web

所以配置如下:

Framework preset: React static
Root directory: apps/web
Build command: npm run build
Build output directory: dist

也就是:

Root directory:
apps/web

Build command:
npm run build

Build output directory:
dist

这样 Cloudflare 会进入 apps/web 目录,然后执行:

npm install
npm run build

构建完成后,把 apps/web/dist 作为网站根目录发布。


十、第一次部署

配置完成后,点击:

Save and Deploy

Cloudflare 会开始构建。

如果成功,会看到类似:

Building application
Deploying to Cloudflare's global network
Success

Cloudflare 会生成一个默认域名:

fiance.pages.dev

可以先访问这个地址测试:

https://fiance.pages.dev

还要测试子页面:

https://fiance.pages.dev/tools/deposit-yield

如果首页和子页面都能正常打开,说明部署成功。


十一、绑定自定义域名

部署成功后,进入项目:

Workers & Pages

fiance

Custom domains

Set up a custom domain

输入自己的域名:

51qdd.com

Cloudflare 会提示创建 DNS 记录。

如果根域名之前已经有 A 记录指向 VPS,例如:

Type: A
Name: 51qdd.com
Content: 38.76.168.151

Cloudflare 会提示将其替换为 Pages 对应的记录。

最终会变成类似:

Type: CNAME
Name: 51qdd.com
Content: fiance.pages.dev
TTL: Auto

如果绑定的是根域名,Cloudflare 可能会提示:

CNAME records normally can not be on the zone apex. We use CNAME flattening to make it possible.

这个不是错误。

意思是普通 DNS 不允许根域名直接使用 CNAME,但 Cloudflare 通过 CNAME Flattening 技术自动处理。

点击:

Activate domain

等待生效。


十二、DNS 切换前要注意什么

如果原来的域名还在 VPS 上运行,例如:

finance.51qdd.com

不要一开始就删除 VPS 上的网站。

建议顺序是:

先部署 Cloudflare Pages

测试 pages.dev 默认域名

绑定自定义域名

确认 51qdd.com 正常访问

再考虑删除 VPS 上的旧站点

这样可以避免网站中断。


十三、旧域名 301 跳转到新域名

我之前还使用过:

js.51qdd.com
finance.51qdd.com

后来主站统一到了:

51qdd.com

为了避免多个域名重复内容影响 SEO,需要把旧域名 301 到主域名。

进入 Cloudflare:

Rules

Redirect Rules

Create Rule

创建规则。

规则名称:

js-to-root

匹配条件使用 Custom filter expression:

http.host eq "js.51qdd.com"

目标跳转到:

https://51qdd.com

状态码选择:

301 Permanent Redirect

如果需要保留路径,效果应该是:

https://js.51qdd.com/about

https://51qdd.com/about
https://js.51qdd.com/tools/deposit-yield

https://51qdd.com/tools/deposit-yield

如果界面支持保留路径和查询参数,勾选:

Preserve path
Preserve query string

如果需要使用表达式,可以使用类似:

concat("https://51qdd.com", http.request.uri.path)

如果还要保留查询参数,则需要根据 Cloudflare 当前界面选项选择 Preserve query string。


十四、为什么要统一域名

如果同一个网站同时存在多个入口:

51qdd.com
js.51qdd.com
finance.51qdd.com

搜索引擎可能会认为这是重复内容。

比较好的做法是:

51qdd.com          主站
www.51qdd.com      301 到主站
js.51qdd.com       301 到主站
finance.51qdd.com  301 到主站

这样可以把 SEO 权重集中到一个域名。


十五、现在的最终架构

迁移完成后的架构如下:

GitHub

Cloudflare Pages

51qdd.com

静态网站不再依赖 VPS。

香港 VPS 只保留给这些服务使用:

1Panel
OpenClaw
Telegram Bot
需要后端的服务
数据库服务

前端静态站点完全交给 Cloudflare Pages。


十六、日常更新流程

以后开发网站,只需要本地修改代码。

启动本地开发:

npm run dev

浏览器访问:

http://localhost:5173

修改完成后提交:

git add .
git commit -m "优化工具页 SEO 内容"
git push

Cloudflare Pages 会自动检测 GitHub 的更新,然后自动执行:

npm install
npm run build

发布成功后,访问:

https://51qdd.com

即可看到最新版本。


十七、如果构建失败怎么办

Cloudflare Pages 构建失败时,不会影响线上旧版本。

也就是说:

本次构建失败

线上网站仍然保持上一个成功版本

可以进入:

Workers & Pages

项目

Deployments

查看 Build logs

常见错误有:

1. 找不到 package.json

可能是 Root directory 配错了。

如果项目结构是:

fiance/
└── apps/
    └── web/
        └── package.json

Root directory 应该填:

apps/web

2. 找不到 dist

可能是 Build output directory 配错了。

如果构建后生成:

apps/web/dist

并且 Root directory 是:

apps/web

那么 Build output directory 应该填:

dist

3. npm run build 失败

需要本地先测试:

npm run build

如果本地都失败,Cloudflare 也会失败。

先修复本地构建问题,再提交。


十八、React 路由刷新 404 的问题

在 VPS + Nginx 部署时,需要配置:

location / {
    try_files $uri $uri/ /index.html;
}

Cloudflare Pages 对常见静态站点支持更好,但如果依然遇到子页面刷新 404,可以在项目 public 目录下新增 _redirects 文件。

路径:

apps/web/public/_redirects

内容:

/* /index.html 200

这样构建后 _redirects 会进入 dist 目录,Cloudflare Pages 会按照这个规则处理路由。

适用于这种场景:

访问 /tools/deposit-yield

Cloudflare 找不到真实文件

回退到 /index.html

React Router 接管路由

十九、robots.txt 配置

为了方便搜索引擎抓取,可以在 public 目录创建:

apps/web/public/robots.txt

内容:

User-agent: *
Allow: /

Sitemap: https://51qdd.com/sitemap.xml

二十、sitemap.xml 配置

可以在 public 目录创建:

apps/web/public/sitemap.xml

示例:

<?xml version="1.0" encoding="UTF-8"?>
<urlset
  xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

  <url>
    <loc>https://51qdd.com/</loc>
    <priority>1.0</priority>
  </url>

  <url>
    <loc>https://51qdd.com/about</loc>
    <priority>0.8</priority>
  </url>

  <url>
    <loc>https://51qdd.com/privacy</loc>
    <priority>0.8</priority>
  </url>

  <url>
    <loc>https://51qdd.com/disclaimer</loc>
    <priority>0.8</priority>
  </url>

  <url>
    <loc>https://51qdd.com/contact</loc>
    <priority>0.8</priority>
  </url>

  <url>
    <loc>https://51qdd.com/tools/deposit-yield</loc>
    <priority>0.9</priority>
  </url>

</urlset>

如果工具页面很多,建议用脚本自动生成 sitemap。


二十一、Google Search Console

部署完成后,可以把站点添加到 Google Search Console。

地址:

https://search.google.com/search-console

添加属性:

51qdd.com

如果域名 DNS 在 Cloudflare,推荐使用 DNS 验证。

验证完成后,提交:

https://51qdd.com/sitemap.xml

然后等待 Google 抓取和收录。

可以用以下命令检查是否收录:

site:51qdd.com

二十二、Google AdSense 前的准备

如果后续要申请 Google AdSense,建议网站至少具备:

关于我们页面
隐私政策页面
免责声明页面
联系我们页面
15 个以上有实际功能的工具页面
每个工具页面有说明文字
每个工具页面有 FAQ
robots.txt
sitemap.xml
HTTPS
移动端适配

不要刚上线就马上放广告。

更稳妥的做法是:

先完善内容

提交 Google Search Console

等待部分页面收录

再申请 AdSense

二十三、Cloudflare Pages 和 VPS 的区别

VPS 部署:

需要自己维护服务器
需要自己管理 Nginx
需要自己处理证书
需要手动上传文件
服务器宕机会影响网站

Cloudflare Pages:

不需要服务器
自动 HTTPS
自动 CDN
自动构建
自动发布
GitHub 推送后自动上线

对于纯静态网站,Cloudflare Pages 更省心。


二十四、适合 Cloudflare Pages 的网站

适合:

个人博客
工具站
文档站
导航站
产品官网
作品集
React/Vue/Vite 静态站
Astro 博客
Hugo 博客
Hexo 博客

不适合:

WordPress
论坛
需要数据库的网站
需要用户登录的网站
需要支付系统的网站
需要文件上传的网站

如果后续需要后端,可以考虑:

Cloudflare Workers
Supabase
自建 VPS API

二十五、最终总结

这次迁移后,我的网站从传统的 VPS 静态部署,变成了更现代的自动化部署流程。

最终流程:

本地开发

GitHub 提交

Cloudflare Pages 自动构建

自动发布到 51qdd.com

日常更新只需要:

git add .
git commit -m "更新内容"
git push

Cloudflare Pages 会自动完成后面的事情。

对于个人工具站和博客来说,这种方式非常适合:

成本低
维护少
速度快
自动化程度高

目前我的网站主站已经迁移到:

https://51qdd.com

后续重点就不再是服务器运维,而是:

持续增加工具
优化 SEO
完善内容
提交搜索引擎
申请 AdSense
返回文章列表 访问主站 51qdd.com