这次的起因很日常:一晚上写了 7 篇文章,凌晨一点半推上去,当场验了一下线上——好家伙,首页没有、搜索搜不到、直接访问 404,7 篇文章集体蒸发。排查到最后,发现这个 bug 我已经带着跑了十几篇文章,只是每次都被我自己”睡好”了。
现象:本地一切正常,线上集体蒸发
那天晚上的时间线是这样的:
- 一口气写完 7 篇文章,front matter 日期都署了当天
2026-08-31 - 凌晨 1:39 推送,GitHub Actions 和 Pages 构建全部绿勾 ✅
- 打开首页——没有新文章
- 直接访问文章链接——404
- 一开始怀疑是不是构建缓存的问题,反复刷了几次,依然没有
最诡异的是:本地 jekyll serve 一切正常,7 篇文章都在,首页、归档、搜索全都能看到。同一个仓库,同一个配置,线上就是没有。
排查:从”是不是没推上去”到”时区”
先排除了最低级的原因:
- ✅ push 成功,GitHub 上能看到新文件
- ✅ Pages 构建成功,没有报错
- ✅ front matter 里没有
published: false这种手滑标记 - ✅ 文件名日期格式
2026-08-31-xxx.md没写错
然后注意到一个线索:站点的全文搜索索引是一个 JSON 文件,数了数里面只有 20 篇文章,而本地构建是 27 篇。差的那 7 篇,恰好就是刚推的 7 篇。
再看构建日志里的时间戳:2026-08-30T17:39:36Z。
结尾那个 Z,是 UTC。
而当时北京时间已经是 8 月 31 日凌晨 1:39,UTC 才刚到 8 月 30 日 17:39。文章署的日期是 8 月 31 日——对 GitHub Pages 的构建机来说,这是一批来自未来的文章。
原理:Jekyll 默认不发”未来”的文章
Jekyll 有个叫 future 的配置项,默认是 false:构建时会把日期大于当前时刻的文章当作”还没到发布时间”,直接跳过。这个设计的本意是支持”定时发布”——你可以提前写好一堆文章,日期署未来,到了日子它们就自动出现。
问题在于时区的换算:
你的文章日期: 2026-08-31 00:00(Jekyll 按 UTC 零点解析)
GitHub Pages: 按 UTC 构建
你本人: 住在 UTC+8
触发条件:北京时间 0:00 ~ 8:00 之间,推送署名为"今天"的文章
(此时 UTC 还是昨天,你的"今天"= GitHub 的"未来")
也就是说,文章会在”未来”里潜伏到 UTC 零点——对应北京时间早上 8 点——之后的第一次构建才会被放出来。
最有意思的部分:用 git log 做了一次作息统计
查到这里我本来想骂一句”怎么不早说”,然后顺手翻了下自己的提交历史,画面突然变得好笑起来:
2026-08-24 00:08 add DeepSeek Harness plugins docs
2026-08-21 00:28 add Pi Agent and OpenCode comparison
2026-08-12 00:33 add Chat Completions and Responses API posts
2026-08-07 00:35 add Codex Bailian image generation post
2026-08-05 00:54 add new post
2026-08-19 01:27 add images for DeepSeek Harness documentation
清一色的凌晨零点半。也就是说,这个 bug 我已经踩了不知道多少次了:几乎每一篇署名”今天”的文章,发布后的头 7 个小时在线上都是 404。
那为什么从来没发现过?因为我的作息替我兜了底:
01:30 写完推送,文章在线上"潜伏"
01:45 睡觉 😴
09:00 起床,UTC 早已跨过零点,文章早就自动出现了
→ "嗯,发布成功",从来没有怀疑过
每次都是推完就睡,醒来文章已经”自愈”。这个 bug 跟了我十几篇文章,唯一的目击者是我睡觉时的 GitHub Actions。
这次能抓到它,纯粹是因为推完当场做了验证——顺手 curl 了一下线上的搜索索引,发现少了 7 篇,坑才藏不住。
修复:一行配置
在 _config.yml 里加一行:
# 构建时包含"未来日期"的文章:作者在 UTC+8 写作,文章日期是当天,
# 而 GitHub Pages 按 UTC 构建,晚上发布时当天日期在 UTC 仍是"未来"。
future: true
对个人博客来说,日期只是排序展示用,不存在”写好一篇定时到下周再发”的需求,关掉这个过滤零副作用。如果你的场景真需要定时发布(比如多人协作的官方博客),那就别改配置,改成深夜写的文章日期署前一天就好。
复盘
总结一下这个 bug 的三个有趣之处:
- 本地永远复现不了。
jekyll serve默认包含未来文章,和生产的jekyll build行为相反,”我本地是好的”这句话在这里天然失效。 - 它会自愈。几小时后自动恢复,没有报错、没有日志告警,属于最难受的一类问题:不出错,只是”暂时不存在”。
- 触发条件精确绑定作息。只有”北京时间 0 点到 8 点推当天文章”才触发,而恰好这个时间段推文的人——作息稳定地把它掩盖了过去。
最后还有一个冷笑话:这篇文章本身也署名 8 月 31 日,也是凌晨写的。如果 future: true 这个修复没跟着一起推上去,你現在应该看不到这篇文章——它正在未来里等天亮。
所以,如果你能看到这里,说明修复生效了。🙂