部署到 Cloudflare
Cloudflare 是 Jant 推荐的部署平台。两条路径任选其一:
- 一键部署:点 Deploy 按钮,约 5 分钟完成。资源全部由 Cloudflare 在你账号下自动创建。
- 本地开发后再部署:先在本地跑通,再用 Wrangler 上线。适合想先调主题或做离线调试的人。
部署完成后还要绑定自定义域名、配置 R2 公开访问,否则媒体会经 Worker 中转,消耗免费额度。
想部署在自己的服务器上,见 使用 Docker 部署。
占位符约定
正文里所有 <...> 都需要替换成你自己的值。
| 占位符 | 含义 |
|---|---|
<github-user> |
GitHub 用户名 |
<repo> |
一键部署生成的仓库名 |
<project> |
Worker 项目名(一键部署默认 my-site) |
<account> |
Cloudflare 子域前缀,部署后由 Cloudflare 给出 |
<your-domain> |
你的自定义域名 |
<media-domain> |
R2 媒体子域,例如 media.<your-domain> |
<database-id> |
wrangler d1 create 输出的 D1 数据库 ID |
前置条件
- Cloudflare 账号:dash.cloudflare.com 注册或登录。
- GitHub 账号:一键部署会把代码托管到 GitHub。前往 github.com 注册或登录。
- 启用 R2:进入 R2 控制台,点 Enable R2 接受条款。这一步必须先做——跳过的话,部署会报
uses R2 which is only available with an R2 subscription。R2 用于存放上传的图片和视频,免费额度 10 GB 存储 + 每月 100 万次读取。 - 自定义域名(推荐):托管在同一个 Cloudflare 账号下。如果还没有,先把域名的 DNS 托管到 Cloudflare。
- 本地开发路线还需要:Node.js 24+、
git、openssl。
一键部署
点 Deploy 按钮后,Cloudflare 会按表单内容自动创建 GitHub 仓库、D1 数据库、R2 存储桶,并完成首次部署。
表单填写
| 字段 | 说明 |
|---|---|
| Git account | 选你的 GitHub 账号。首次使用时点 New GitHub Connection 完成授权,Cloudflare 会自动创建仓库。 |
| Project name | 默认为 my-site。这个值同时是站点子域 <project>.<account>.workers.dev 和 GitHub 仓库名。现在就改成你要用的名字,例如 my-blog。 |
| D1 database | 保持默认 Create new,名称用默认值。 |
| Database location hint | 选大多数读者所在的区域。每个页面都要等这个数据库,见 启用 D1 读复制。 |
| R2 bucket | 保持默认 Create new,名称用默认值。 |
| AUTH_SECRET | 保留自动生成的值,或换成自己的 32 字节以上随机字符串。它用于会话签名,上线后不要修改。 |
部署完成后,Cloudflare 会显示形如 https://<project>.<account>.workers.dev 的地址。先不要创建管理员账号——下一步绑定自定义域名后再创建,避免会话在切换域名时失效。
克隆到本地(推荐)
一键部署已经在你的 GitHub 账号下建好了仓库。克隆到本地后可以:
- 改
wrangler.toml里的环境变量(下一节会用到) - 改主题、加页面、升级依赖
- 推送到
main,自动触发重新部署
git clone [email protected]:<github-user>/<repo>.git
cd <repo>
npm install
不想克隆也行,下一节的所有配置都能在 Cloudflare 控制台完成。
上线前必做
完成下面四步后再创建管理员账号。
1. 绑定自定义域名
- 打开 Workers & Pages,选中你的 Worker。
- 进入 Settings → Domains & Routes → Add。
- 填入你的域名
<your-domain>。
域名在同一个 Cloudflare 账号下时,DNS 记录会自动写入,证书通常 1–2 分钟内签发。
2. 配置 R2 公开访问
不配置时,每次访问图片或视频都要经 Worker 中转,速度慢且消耗 Worker 配额。配置 R2_PUBLIC_URL 后,媒体直接从 R2 公开域返回。
给 R2 存储桶绑定一个公开子域
- 打开 R2 控制台,点开你的存储桶。
- Settings → Public access → Custom Domains → Connect Domain。
- 填一个属于你域名的子域,例如
media.<your-domain>。 - Cloudflare 自动写入 CNAME 并签发证书。状态从
Initializing变为Active后,完整复制 Public URL(形如https://<media-domain>,末尾没有斜杠)。
不要使用 r2.dev 的临时公开 URL——它有速率限制,不能用于生产。
如果状态长时间停在 Initializing,通常是该子域已被其他 DNS 记录占用——到 Cloudflare DNS 页面删除冲突记录后会自动恢复。
把 URL 配给 Worker,任选其一:
控制台:Workers & Pages → 选中 Worker → Settings → Variables and Secrets → Add。名称
R2_PUBLIC_URL,值https://<media-domain>。保存后会自动重新部署。项目的wrangler.toml设了keep_vars = true,以后部署时这个变量才会保留;没有这一行,每次部署都会删掉文件里没列出的变量。代码:在
wrangler.toml的[vars]段下加:[vars] R2_PUBLIC_URL = "https://<media-domain>"推送到
main,或运行npm run deploy。
验证:打开一张已上传的图片,页面源码里的 <img src> 应该是 https://<media-domain>/...,而不是 <project>.<account>.workers.dev/...。
3. 启用 D1 读复制
Jant 渲染一个页面要依次执行十来次 D1 查询,Worker 和数据库之间的一次往返因此要付十来次。不开读复制(read replication)时,所有查询都发往数据库所在的那一个区域,离它远的读者打开每个页面都要等。两个版本相同的 Jant 站点,数据库都在北美西部,从东京测得:
| 读复制 | 单次查询 | 首页 |
|---|---|---|
| 关 | 约 125 ms | 约 1.0 s |
| 开 | 约 20 ms | 约 0.4 s |
开启读复制后,D1 会在它的每个区域各放一份只读副本,读请求交给最近的那一份。副本不额外收费,D1 照常按读写的行数计费。
开启方法:
- 打开 D1,选中数据库。
- Settings → Enable Read Replication。
用 API 开启时,把 read_replication.mode 设为 "auto",做法见 Cloudflare 的 读复制文档。wrangler.toml 不用改:读复制要求通过 D1 Sessions API 读取数据,Jant 已经这样做了。
写入仍然发往主库;登录后发出的请求也从主库开始读,以免落后的副本把你登出,所以你看到的页面会比读者慢。
检查一个站点:对比不访问数据库的 /healthz 和执行一次 D1 查询的 /readyz。下面五个请求,curl 会复用同一个连接:
curl -s -o /dev/null -w '%{url_effective} %{time_total}s\n' 'https://<your-domain>/{healthz,healthz,readyz,readyz,readyz}'
第一行包含建立连接的时间。最快的那次 readyz 减去第二次 healthz,就是你所在位置的读者一次查询的耗时:上面两个站点分别约 125 ms 和 20 ms。
4. 启用图片自动缩放(可选)
开启 Image Transformations 后,Jant 会按访问者的屏幕宽度和像素密度请求对应尺寸,避免手机加载 4000px 原图。
每月前 5000 次变换免费,超出后约 $0.50 / 1000 次。每张原图按需衍生 3–5 个尺寸,个人站基本不会触发付费。
图片变换是域名(zone)级功能,需要先按第 2 步配置好 R2 自定义域名。
- 在 Cloudflare 控制台 选中你绑定的域名。
- 左侧菜单 → Images → Transformations。
- 把对应域名开关切到 On(首次启用会弹出条款确认)。
把 IMAGE_TRANSFORM_URL 加到 Worker(添加方式同 R2_PUBLIC_URL),值为:
https://<media-domain>/cdn-cgi/image
初始化管理员
打开 https://<your-domain>,按提示创建管理员账号、设置站点名称。
更多可配置项见 配置。
本地开发后再部署
想先在本地搭好再上线,走这条路径。
npm create jant@latest jant-site
cd jant-site
pnpm 或 yarn 同样可用:
pnpm create jant@latest jant-site
yarn create jant@latest jant-site
要建在当前目录,传 .:
npm create jant@latest .
create-jant 会安装依赖、初始化 git(目录已在仓库中时跳过)、生成本地 .dev.vars(含本地 AUTH_SECRET),并创建已绑定 D1 和 R2 的 Wrangler 项目。
本地启动
npm run dev
打开 http://localhost:3000,按提示完成初始化(管理员账号、站点名、语言)。
换端口:
PORT=8787 npm run dev
部署到 Cloudflare
按顺序执行:
登录 Wrangler
npx wrangler login浏览器会跳转到授权页面,授权后终端显示
Successfully logged in。创建 D1 数据库
npx wrangler d1 create <project>-db把输出中的
database_id写到wrangler.toml里[[d1_databases]]段:[[d1_databases]] binding = "DB" database_name = "<project>-db" database_id = "<database-id>"database_name必须与刚才传给d1 create的名字一致。想让数据库靠近读者,加上
--location指定读者所在的区域:wnam、enam、weur、eeur、apac或oc。不加时 D1 会选离你近的区域。创建 R2 存储桶
npx wrangler r2 bucket create <project>-mediawrangler.toml里[[r2_buckets]]的bucket_name必须与之一致:[[r2_buckets]] binding = "R2" bucket_name = "<project>-media"设置生产
AUTH_SECRET.dev.vars里的密钥只用于本地。生产密钥用下面的命令写入:openssl rand -base64 32 | npx wrangler secret put AUTH_SECRET一旦站点上线,不要修改这个值——会让所有现存会话失效。
部署
npm run deploynpm run deploy调用jant deploy,依次:- 应用 D1 迁移和数据补丁。
- 上传 Worker 代码与静态资源。
成功后终端会打印形如
https://<project>.<account>.workers.dev的地址。回到 上线前必做 完成自定义域名和 R2 公开访问,再创建管理员。
进阶配置
通过 GitHub Actions 自动部署
create-jant 创建的项目里已内置 .github/workflows/deploy.yml。它安装依赖后运行 jant deploy --ci,用的是 lockfile 里的 @jant/core 版本,所以检查和部署本身会随升级更新。先在自己电脑上运行一次 npm run deploy,再提交 wrangler.toml:database_id 还是 "local" 时,workflow 会停下并说明原因,不会部署。然后在 GitHub 仓库加两个 Secret,之后每次推送 main 都会自动部署。没加 Secret 时,workflow 跳过部署,运行结果仍是通过:
CF_API_TOKENCF_ACCOUNT_ID
获取方式:
CF_ACCOUNT_ID:Cloudflare 控制台 → 任意 Worker 或 Pages 项目 → 右侧栏 Account ID。CF_API_TOKEN:API Tokens 页面 → Create Token → 选 Edit Cloudflare Workers 模板(已包含部署 Worker、读写 D1、读写 R2 所需权限)。按提示选账号和域名(如果用了自定义域名),生成后立即复制——关闭页面后无法再次查看。
添加到 GitHub:仓库 → Settings → Secrets and variables → Actions → New repository secret。
一键部署创建的仓库里也有这个 workflow。Cloudflare 已经会在每次推送时部署它,所以这种仓库不要加这两个 Secret,加了每次推送会部署两次。
部署在子路径下
把 Jant 挂在某个子路径下(如 <your-domain>/blog),主域留给其他服务,需要两步:
告诉 Jant 子路径前缀:
[vars] SITE_PATH_PREFIX = "/blog"Jant 会在部署时把带前缀的静态资源准备到
/blog/_assets/*。在 Cloudflare 控制台 → 选中你的域名 → Workers Routes → Add route,填
<your-domain>/blog*,Worker 选你的 Jant Worker。
SITE_PATH_PREFIX 与 SITE_ORIGIN 是两个独立变量。SITE_ORIGIN 只接受 origin(scheme + host + port),带路径时站点会在启动时停下——子路径必须通过 SITE_PATH_PREFIX 设置。详细说明见 配置 § 公开 URL 和子路径。
升级
用 npm install @jant/core@latest 安装新版本,然后推送到 main,或者运行 npm run deploy。升级前要备份什么、0.x 的站点要改什么,见升级。
常见错误
uses R2 which is only available with an R2 subscription:没启用 R2,到 R2 控制台 接受条款。Authentication error [code: 10000]:没登录或 token 过期,重新npx wrangler login。database_id "..." is invalid:wrangler.toml里的database_id占位符没替换成wrangler d1 create输出的真实 ID。A worker with this name already exists:改wrangler.toml顶部的name。- 自定义域名长时间 SSL pending:该子域已被其他 DNS 记录占用,到 DNS 页面删冲突记录后自动恢复。