Three post-deploy checks I run after every Cloudflare Pages build

Three post-deploy checks I run after every Cloudflare Pages build

我在每次 Cloudflare Pages 构建后运行的三项部署后检查

After spending two weeks debugging issues that only showed up in production — a sitemap _redirects rule that was blocking my own sitemap-index.xml and a Bluesky image upload race against Cloudflare Pages deploy lag — I added three post-deploy checks to my workflow. They’re fast and specific to the failure modes I’ve actually hit, not a full end-to-end test suite. Three sites (aiappdex.com, findindiegame.com, ossfind.com) on Cloudflare Pages with Astro 5 SSG. Here’s what I check.

在花费两周时间调试那些仅在生产环境中出现的问题(例如一个阻塞了我自己的 sitemap-index.xml 的 sitemap _redirects 规则,以及 Bluesky 图片上传与 Cloudflare Pages 部署延迟之间的竞态条件)后,我在工作流中增加了三项部署后检查。这些检查速度快且针对我实际遇到的故障模式,而非完整的端到端测试套件。我目前在 Cloudflare Pages 上使用 Astro 5 SSG 托管了三个站点(aiappdex.com, findindiegame.com, ossfind.com),以下是我检查的内容。

Check 1: Sitemap reachability

检查 1:站点地图(Sitemap)可达性

The simplest check and the one I should have had from day one. After a Cloudflare Pages deploy, I verify that sitemap-index.xml is reachable and returning 200 on all three domains:

这是最简单的检查,也是我从第一天起就应该做的。在 Cloudflare Pages 部署完成后,我会验证 sitemap-index.xml 在所有三个域名上是否可达并返回 200 状态码:

for domain in aiappdex.com findindiegame.com ossfind.com; do
  status=$(curl -s -o /dev/null -w "%{http_code}" "https://$domain/sitemap-index.xml")
  echo "$domain/sitemap-index.xml → $status"
  if [ "$status" != "200" ]; then
    echo "FAIL: $domain sitemap unreachable"
  fi
done

I also check sitemap-0.xml — the actual URL sub-sitemap that @astrojs/sitemap generates — and assert that it contains at least a minimum expected URL count. For aiappdex.com that threshold is 1,000; if it drops below that after a deploy, the ETL data pipeline probably broke silently. The reason this check exists: I had a _redirects rule rewriting sitemap-index.xmlsitemap-0.xml as an emergency workaround that turned out to be wrong. It was live for five days before I found it. The rule was blocking the real sitemap-index.xml from reaching crawlers while appearing fine in the browser (which followed the redirect). Curl with -o /dev/null -w "%{http_code}" doesn’t follow redirects by default, so it would have caught this immediately.

我还会检查 sitemap-0.xml(由 @astrojs/sitemap 生成的实际 URL 子站点地图),并断言它包含至少预期的最小 URL 数量。对于 aiappdex.com,该阈值为 1,000;如果部署后低于此值,说明 ETL 数据管道可能已静默失效。设置此检查的原因是:我曾为了应急,写了一条将 sitemap-index.xml 重定向到 sitemap-0.xml_redirects 规则,结果证明是错误的。它在线运行了五天我才发现。该规则阻止了爬虫访问真实的 sitemap-index.xml,但在浏览器中看起来却一切正常(因为浏览器会自动跟随重定向)。而使用 curl -o /dev/null -w "%{http_code}" 默认不会跟随重定向,因此它能立即捕捉到这个问题。

Check 2: IndexNow batch submission

检查 2:IndexNow 批量提交

After every successful sitemap check, I run node scripts/indexnow.mjs. The script reads the live sitemap XML from each domain, collects all URLs, and POSTs them to the IndexNow endpoint for Bing, Yandex, Naver, and Seznam using site-specific keys.

在每次站点地图检查成功后,我会运行 node scripts/indexnow.mjs。该脚本会读取每个域名的实时站点地图 XML,收集所有 URL,并使用特定站点的密钥将它们 POST 到 Bing、Yandex、Naver 和 Seznam 的 IndexNow 端点。

Output looks like: 输出如下:

aiappdex.com: submitted 1179 URLs → 200 OK
findindiegame.com: submitted 139 URLs → 200 OK
ossfind.com: submitted 144 URLs → 200 OK

If a site returns 403 from IndexNow it usually means the key verification file (/<key>.txt) wasn’t deployed correctly or a _redirects rule is mangling the path. Catching this right after deploy matters because the IndexNow key-verification window isn’t instantaneous — letting it sit in a broken state delays indexing. I wrote more about the IndexNow setup in this week’s tools post. I run this manually after deploy rather than inline in the GitHub Actions workflow because the Cloudflare Pages build takes 2-3 minutes, and IndexNow works best with live URLs. Running it as a separate workflow_dispatch trigger after the deployment succeeds means I’m submitting URLs that are actually live rather than ones that might still be deploying.

如果站点从 IndexNow 返回 403,通常意味着密钥验证文件(/<key>.txt)未正确部署,或者 _redirects 规则破坏了路径。在部署后立即捕捉到这一点很重要,因为 IndexNow 的密钥验证窗口并非即时的——让它处于损坏状态会延迟索引。我在本周的工具文章中详细介绍了 IndexNow 的设置。我选择在部署后手动运行此脚本,而不是将其内联到 GitHub Actions 工作流中,因为 Cloudflare Pages 构建需要 2-3 分钟,而 IndexNow 在处理实时 URL 时效果最好。在部署成功后将其作为单独的 workflow_dispatch 触发器运行,意味着我提交的是已经上线的 URL,而不是可能仍在部署过程中的 URL。

Check 3: Weekly Lighthouse spot-check

检查 3:每周 Lighthouse 抽查

The third check runs on a cron — Monday 04:30 UTC — not after every deploy. It’s slower (3-4 minutes per site, nine URLs total), so daily would be wasteful for a static site that doesn’t change at runtime. The workflow uses treosh/lighthouse-ci-action with one homepage and one deep entry page per site.

第三项检查按计划任务(Cron)运行——每周一 04:30 UTC——而不是每次部署后都运行。它比较慢(每个站点 3-4 分钟,总共九个 URL),因此对于运行时不会改变的静态站点来说,每天运行是浪费资源。该工作流使用 treosh/lighthouse-ci-action,每个站点检查一个主页和一个深度入口页面。

I’m watching for Performance below 80, CLS above 0.1, or accessibility score regression. Astro SSG with no client-side JS should hold steady on all three — if they slip it means something in Tailwind v4 config or the ad slot component changed the layout paint behavior. The results upload to temporaryPublicStorage so I can diff before/after on regressions. I don’t set hard failure thresholds that block deploys. These sites are pre-revenue with essentially zero traffic right now; blocking a deploy because a Lighthouse score dropped from 94 to 88 would be disproportionate. I treat Lighthouse as a trend monitor, not a gate.

我关注的是性能低于 80、CLS 高于 0.1 或可访问性评分下降的情况。没有客户端 JS 的 Astro SSG 在这三项指标上应该保持稳定——如果它们下滑,意味着 Tailwind v4 配置或广告位组件改变了布局渲染行为。结果会上传到 temporaryPublicStorage,以便我可以对比回归前后的差异。我没有设置会阻塞部署的硬性失败阈值。这些站点目前处于预营收阶段,几乎没有流量;仅仅因为 Lighthouse 分数从 94 降至 88 就阻止部署是不成比例的。我将 Lighthouse 视为趋势监控器,而不是准入关卡。

What I’m deliberately not checking

我刻意不检查的内容

  • No uptime monitoring — I’m relying on Cloudflare’s own infrastructure status.

  • No end-to-end user flow tests.

  • No API availability checks — the Turso DB is only queried at build time in SSG mode, so there’s nothing to check at runtime.

  • 没有正常运行时间监控 —— 我依赖 Cloudflare 自身的基础设施状态。

  • 没有端到端用户流程测试。

  • 没有 API 可用性检查 —— Turso 数据库仅在 SSG 模式的构建时进行查询,因此在运行时无需检查任何内容。

For a dynamically rendered site, those gaps would matter. For a static CDN deployment where the entire runtime is pre-built HTML, CSS, and a handful of JSON files, the three checks above cover the actual failure surface I’ve encountered. The publish pipeline has its own idempotency layer (it reads published_urls from article frontmatter and skips already-distributed posts), so I don’t need to verify cross-posting state after each deploy. That’s a separate concern.

对于动态渲染的站点,这些缺口很重要。但对于一个整个运行时都是预构建的 HTML、CSS 和少量 JSON 文件的静态 CDN 部署来说,上述三项检查涵盖了我所遇到的实际故障面。发布管道有其自己的幂等层(它从文章的 frontmatter 中读取 published_urls 并跳过已分发的文章),因此我不需要在每次部署后验证交叉发布的状态。那是另一个层面的问题。

Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.

这是运行三个 AI 策划目录网站的 6 个月持续实验的一部分。文中的技术主张是真实的;本文由 AI 辅助撰写。