Hugo 的「最后更新时间」为什么每次部署都会变成今天
这个问题一开始不明显。
部署完顺手点开一篇文章,发现「最后更新时间」显示的是当天日期,以为是自己刚改过文件,就没在意。过了几天再看,所有文章的更新时间都变成了最近一次部署的日期——包括那些几个月没动过的。
现象
文章页上有两行时间:
- 发布时间:来自 front matter 里的
date,正常 - 最后更新时间:模板里的
{{ .Lastmod }},每次部署后都变成部署当天
也就是说,这个字段不再表示「内容什么时候改过」,而变成了「站点什么时候构建过」。
先看 Lastmod 是怎么算出来的
Hugo 的 .Lastmod 是一套回退顺序,我的 hugo.toml 里原本写着:
[frontmatter]
lastmod = [":git", "lastmod", ":fileModTime", ":default"]
含义是从左往右找第一个能取到值的来源:git 提交信息 → front matter 里的 lastmod → 文件修改时间 → 默认顺序(也就是 date 等)。
按这个顺序,:git 应该最先命中。用 hugo config 确认配置确实生效了:
hugo config | grep -i lastmod
# lastmod = [':git', 'lastmod', ':filemodtime', 'date', ...]
问题就在这里:如果 :git 命中了,页面上应该是提交日期。既然显示的是文件修改时间,说明 GitInfo 没拿到数据,一路退到了 :fileModTime。
拿 git 自己对一下
挑一篇文章,问 git 它最后一次改动是什么时候:
git log -1 --format='%h %aI %s' -- 'content/随笔/hello.md'
# d3a7cd9 2026-05-14T17:34:13+08:00 Vendor site content and theme for Netlify
提交日期是 2026-05-14,页面渲染出来的却是构建当天。GitInfo 确实是空的。
缩小范围
为了分清是环境问题还是仓库问题,我建了一个最小的 Hugo 站点,在同一次提交里放三种文件:
content/a.md (ASCII 文件名)
content/随笔.md (中文文件名)
content/技术/ad.md (中文目录 + ASCII 文件名)
模板里直接打印结果:
{{ if .GitInfo }}{{ .GitInfo.AuthorDate.Format "2006-01-02" }}{{ else }}NIL{{ end }}
跑出来是这样:
a.md GitInfo=2024-03-05 Lastmod=2024-03-05
随笔.md GitInfo=NIL Lastmod=0001-01-01
技术/ad.md GitInfo=NIL Lastmod=0001-01-01
ASCII 文件名一切正常,只要路径里带中文,GitInfo 就是空的。
原因
macOS 的文件系统返回的文件名是 NFD 编码(一部分字符被拆成「基字符 + 组合符号」),而 git 仓库里存的是 NFC。
Hugo 拿着系统给的文件路径去问 git「这个文件最后一次提交是什么时候」,两个编码对不上,git 查不到这个文件,Hugo 就当作没有 GitInfo,继续往下一个回退项走。
整个过程不报错、不打日志,页面照常生成,只是时间不对。这个组合——macOS + 中文目录 + Hugo GitInfo——是可以稳定复现的,和 Hugo 版本无关。
还有个容易忽略的点:Netlify 检出代码之后,所有文件的修改时间都是检出时间。所以一旦退到 :fileModTime,线上每篇文章的「最后更新时间」必然等于部署时间;本地预览时则是本地当天日期。两边不一样,机制是同一个。
怎么处理
三个方向,成本和效果不一样:
- 把中文目录改成 ASCII 名(
content/技术/→content/tech/)。GitInfo 会恢复,时间变成真实的提交日期,但文章 URL 全变,需要配 alias 或重定向。 - 每篇文章手动写
lastmod。最准确,代价是每次改动都要记得维护。 - 调整回退顺序,让
date排在:fileModTime前面。改动最小,效果是:没写lastmod的文章,更新时间与发布时间一致。
我选了第三种:这个博客的内容不常改,「最后更新时间稳定等于文章日期」比「显示每次部署的日期」诚实。
[frontmatter]
lastmod = [":git", "lastmod", "modified", "date", ":fileModTime"]
改完以后两个时间会完全一样,看着有点多余,所以顺手让主题在两行相同时不渲染「最后更新时间」:
{{ if and .Lastmod (ne (.Lastmod.Format "2006-01-02") (.Date.Format "2006-01-02")) }}
<span>最后更新时间: <time datetime="...">{{ .Lastmod.Format ... }}</time></span>
{{ end }}
以后哪篇文章真的改了内容、又补了 lastmod,那一行才会重新出现。
小结
- 页面上的时间不对,先看它是从哪个回退项来的,不要直接改模板
- Hugo 的 GitInfo 依赖 git 查得到文件,路径编码不一致就静默失败,不会报错
- 中文目录在 macOS 上是容易踩的点:内容没问题、构建没问题、页面也正常,只有依赖 git 元数据的功能悄悄失效
一个功能「没坏但结果不对」,通常比直接报错更难发现。