飞导航原来部署在 Vercel,新版准备迁到 Cloudflare Pages。页面域名会在 Pages 部署完成后切换,仓库已经收拾好:构建能重复,分支能预览,出问题也退得回去。
下面只记录飞导航现在用到的配置和上线顺序,不写通用的 Cloudflare Pages 入门教程。
Astro 输出纯静态文件
飞导航不需要服务端渲染。astro.config.mjs 明确使用静态输出:
export default defineConfig({
site: "https://fei.im",
output: "static",
integrations: [icon()],
build: {
assets: "assets",
},
});
site 用来生成 canonical 等绝对地址,构建产物写入 dist/。Cloudflare Pages 只托管这个目录,不运行 Astro 服务器。
正式构建命令是:
npm run build
这条命令还会先迁移链接,再做类型检查:
{
"prebuild": "npm run migrate:links",
"build": "astro check && astro build"
}
如果 YAML 解析失败、链接数量不对或文章 frontmatter 不符合结构,部署会在生成页面之前停止。
Cloudflare Pages 的构建设置
连接 Git 仓库后,Pages 只需要填写:
Build command: npm run build
Build output directory: dist
项目没用 Pages Functions,也没有数据库和服务端环境变量。导航和博客都在构建阶段生成。
仓库还提供了直接部署脚本:
{
"deploy:pages": "npm run build && wrangler pages deploy dist --project-name=fei-im"
}
这个命令留给手动发布。正式环境会让 Pages 连接 GitHub:分支提交生成预览地址,合并主分支后再发布。
页面与 favicon 一起发布
飞导航主站只需要一个 Cloudflare 服务:
- Astro 生成
fei.im静态页面; - 构建脚本把 favicon 生成到
public/favicons/; - Cloudflare Static Assets 一次发布页面和图标。
独立的 fei-favicon 项目仍保留给其他项目复用:
workers/favicon/wrangler.jsonc
飞导航本身不再依赖它。只有其他项目需要更新 icons.fei.im 时才运行:
npm run deploy:favicon
飞导航页面现在请求:
/favicons/{domain}.png
npm run build 会先同步图标,再生成页面,因此不需要协调两个部署的先后顺序。
本地开发如何生成图标
直接启动主站:
npm run dev
predev 会先迁移链接,并把图标同步到 public/favicons/。不再需要环境变量或第二个本地服务。
_headers 里放了什么
Cloudflare Pages 会读取 public/_headers。当前全站响应头包括:
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Content Security Policy 只允许页面加载自己的图片,并允许搜索表单提交到 Google、Bing 和 Grok:
default-src 'self';
form-action https://www.google.com https://www.bing.com https://grok.com;
img-src 'self' data:;
object-src 'none';
以后加统计脚本、评论或外部图片时,CSP 也要跟着改。本地开发不会执行这份 Pages 响应头,漏改后通常要到预览环境才会暴露。
不同文件使用不同缓存
Astro 生成的 /assets/* 文件带内容哈希。文件内容变化时,文件名也会改变,因此可以缓存一年:
/assets/*
Cache-Control: public, max-age=31536000, immutable
品牌图标和站点图标使用一周缓存:
/brand/*
Cache-Control: public, max-age=604800
/icons/*
Cache-Control: public, max-age=604800
HTML 没有设置长期缓存。发布新文章或修改导航链接后,访问者不应该继续拿到旧首页。
旧地址如何处理
public/_redirects 目前包含旧首页和关于页的兼容规则。博客上线后,文章使用固定 Markdown 文件名作为路径,例如:
src/content/blog/favicon-worker.md
→ /blog/favicon-worker/
文件名公开后就别随便改。确实要换路径,就在 _redirects 里给旧地址留跳转,否则搜索结果和外部引用都会断。
站点地图由 @astrojs/sitemap 在构建时自动生成。sitemap-index.xml 指向实际页面清单,新增文章后无需再手工维护 XML。
为什么继续使用原仓库
新版仍是飞导航,历史提交也有用,所以继续使用原仓库。
计划中的发布方式是:
- 旧 Hugo 版本留在 Astro 合并前的 Git 历史中;
- Astro 改动先放在独立开发分支;
- 让 Cloudflare Pages 部署该分支的预览;
- 检查导航、博客、404、响应头和 favicon;
- 合并到主分支;
- 最后切换
fei.im域名。
真出问题时直接回到旧标签,不需要强制推送,也不会覆盖仓库历史。
正式切换前要验证什么
当前构建会生成 8 个页面:
- 首页;
- 关于页;
- 博客列表;
- 5 篇博客文章;
- 404 页面。
构建成功只是第一关,上线前还要检查:
- 首页 243 条链接是否完整;
- Google、Bing、Grok 搜索是否指向正确地址;
- 分类锚点是否能定位到对应区域;
- 手机端是否出现横向页面滚动;
/favicons/{domain}.png是否从主站返回图片;- 博客文章直接访问是否为 200;
- 旧地址是否按预期重定向;
- sitemap 中是否包含所有公开文章。
页面和图标属于同一个主站部署;fei-favicon 是留给其他项目的独立服务,不参与飞导航上线。