Hugo RSS에 lastBuildDate가 없고, 개인정보처리방침이 0001년 글로 섞여 있었습니다
이 사이트의 RSS 피드(새 글 목록을 구독기에 알려주는 XML 파일)를 직접 받아서 열어봤습니다. 설정에서 켜두기만 하고 따로 열어볼 일이 없던 파일이었습니다. 빌드도 배포도 오류 없이 끝나고 있었는데 피드 안에 이상한 게 두 개 있었습니다.

하나는 lastBuildDate가 없다는 것, 다른 하나는 개인정보처리방침이 서기 1년에 쓴 글로 들어가 있다는 것이었습니다.
둘 다 원인은 Hugo 내장 템플릿의 조건 한 줄씩이었습니다. 2026-10-04에 Hugo v0.165.0+extended로 확인했습니다.
피드를 받아보니 두 가지가 이상했습니다

라이브 피드를 그대로 받았습니다.
$ curl -sS -o index.xml https://birchholt.com/index.xml
$ grep -c '<lastBuildDate>' index.xml
0
$ grep -oE '<pubDate>[^<]*' index.xml | tail -2
<pubDate>Wed, 26 Aug 2026 00:00:00 +0000
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000
lastBuildDate는 피드 내용이 마지막으로 바뀐 때를 적는 자리입니다.
RSS 2.0 규격에서는 “The last time the content of the channel changed"로 설명하고
반드시 넣어야 하는 값은 아닙니다. 그래도 Hugo 내장 템플릿에는 이 자리가 있는데 제 피드에서는 빠져 있었습니다.
마지막 항목은 이랬습니다.
<item><title>개인정보처리방침</title><link>https://birchholt.com/privacy/</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
글이 아닌 페이지가 피드에 글처럼 들어가 있었고 발행일은 0001년 1월 1일이었습니다.
pubDate는 항목의 발행일입니다. 구독기가 이 값을 어떻게 보여주는지까지는 확인하지 않았습니다.
다만 어느 쪽이든 정상 값은 아닙니다.
내장 템플릿을 열어보니 조건이 ‘홈의 날짜’였습니다

제 layouts/에는 RSS 템플릿이 없어서 Hugo 안에 들어 있는 템플릿이 그대로 쓰입니다.
v0.165.0 태그의 내장 rss.xml 원문을 열었습니다.
{{- if or $.IsHome $.IsSection }}
{{- $pages = $pctx.RegularPages }}
...
{{ if not .Date.IsZero }}
<lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format "..." }}</lastBuildDate>
{{ end }}
...
<pubDate>{{ .PublishDate.Format "..." }}</pubDate>
세 가지가 보였습니다.
lastBuildDate를 넣을지는 피드를 그리는 페이지, 여기서는 홈 자신의.Date로 정합니다. 홈 날짜가 0이면 통째로 빠집니다- 값은 빌드한 시각이 아니라 **항목들 가운데 가장 늦은
.Lastmod(마지막 수정일)**입니다 - 홈 피드는
RegularPages, 그러니까 섹션에 상관없이 사이트의 일반 페이지 전부를 담고 날짜로 거르지 않습니다
두 번째 증상은 세 번째 항목에서 바로 설명이 됩니다. 첫 번째 증상은 홈 날짜가 왜 0인지를 알아야 했습니다.
홈의 날짜가 비는 조건 — git 정보와 커밋된 _index.md

저장소를 로컬 사본으로 복제해 빌드하면 라이브와 바이트 단위로 같은 피드가 나옵니다. 거기에 날짜만 찍어보는 임시 템플릿을 넣었습니다.
home .Date=0001-01-01 00:00:00 +0000 UTC | .Lastmod=2026-10-04 00:29:41 +0900 KST
posts section: Date=0001-01-01 ... IsZero=true
홈의 Date는 0인데 Lastmod는 채워져 있습니다. 이 사이트는 enableGitInfo = true로
파일마다 마지막 커밋 시각을 Lastmod로 읽고 있습니다
(Hugo 설정 문서: “the Lastmod method on a Page object returns the Git author date of the last commit for that file”).
git 정보만 환경변수로 꺼서 다시 빌드했습니다.
설정 그대로(enableGitInfo = true) → home.Date=0001-01-01 home.IsZero=true
HUGO_ENABLEGITINFO=false → home.Date=2026-09-09 home.IsZero=false
원인은 Hugo 소스에 있었습니다. 홈이나 섹션 같은 목록 페이지는 날짜 네 개(Date·Lastmod·PublishDate·ExpiryDate)가 전부 0일 때만 하위 글의 날짜를 끌어옵니다 (content_map_page_assembler.go, IsAllDatesZero).
wasZeroDates := pageBundle.m.pageConfig.Dates.IsAllDatesZero()
content/_index.md가 git에 커밋돼 있으면 git 정보가 그 파일의 Lastmod를 채웁니다.
그러면 “전부 0"이 아니게 돼서 Date를 물려받지 못하고 그대로 0이 남습니다.
이 조건이 맞는지 보려고 글 두 편과 날짜 없는 페이지 하나만 둔 빈 사이트를 만들어 경우를 나눠봤습니다.
_index.md | git 정보 | lastBuildDate |
|---|---|---|
| 없음 | 끔 | Sat, 05 Sep 2026 00:00:00 +0000 |
| 없음 | 켬 | Thu, 01 Oct 2026 10:00:00 +0900 |
| 있음, 커밋 안 함 | 켬 | 있음 |
| 있음, 커밋함 | 켬 | 없음 |
| 있음, 커밋함 | 끔 | 있음 |
세 가지가 겹쳐야 빠집니다. git 정보 켜기, 홈 _index.md 만들기, 그 파일 커밋하기입니다.
로컬에서 _index.md를 막 만들고 아직 커밋하지 않았을 때는 멀쩡하다가
커밋한 다음 빌드부터 조용히 사라집니다. 빌드 경고는 없었습니다.
0001년 항목은 날짜 없는 일반 페이지였습니다

content/privacy.md의 머리말(front matter)에는 date가 없습니다.
글이 아니라 안내 페이지라서 날짜가 없습니다.
같은 덤프에서 이 페이지는 Date=0001-01-01 Pub=0001-01-01 Kind=page Section=으로 나옵니다.
섹션은 없어도 **종류(Kind)는 글과 같은 page**라서 RegularPages에 들어가고
PublishDate가 0이니 Go의 기본 시각인 0001년 1월 1일이 그대로 찍힙니다.
여기에 날짜를 적어 넣는 건 답이 아닙니다. 그러면 0001년은 사라져도 개인정보처리방침이 새 글처럼 피드 위쪽으로 올라옵니다. 피드에서 빼는 게 맞습니다.
내장 템플릿을 덮어써서 고쳤습니다

RSS 템플릿 문서대로 layouts/home.rss.xml을 만들고 내장 템플릿을 그대로 복사했습니다.
문서 예시가 home.rss.xml이라 그 이름을 썼는데 layouts/rss.xml로 둬도 출력은 바이트 단위로 같았습니다.
바꾼 곳은 두 군데입니다.
+{{- $pages = where $pages "Date.IsZero" false }}
{{- $limit := .Site.Config.Services.RSS.Limit }}
...
- {{ if not .Date.IsZero }}
- <lastBuildDate>{{ (index $pages.ByLastmod.Reverse 0).Lastmod.Format ...
+ {{ with $pages }}
+ <lastBuildDate>{{ (index .ByLastmod.Reverse 0).Lastmod.Format ...
첫 줄은 날짜 없는 페이지를 거릅니다. 개수 제한(limit)보다 먼저 걸러야
제한이 있을 때도 걸러진 페이지가 자리를 차지하지 않습니다.
두 번째는 조건을 홈의 날짜 대신 ‘항목이 하나라도 있는가’로 바꿉니다. 값을 계산하는 식은 원래와 같습니다.
저장소 사본에서 같은 옵션(--gc --minify)으로 빌드해 전후를 비교했습니다.
lastBuildDate item 0001년 sitemap의 /privacy/
수정 전 없음 13 1 있음
수정 후 Sun, 04 Oct 2026 00:29:41 +0900 12 0 있음 (sitemap 파일 diff 없음)
xmllint --noout도 통과했습니다. 개인정보처리방침 페이지 자체는 그대로 만들어지고 sitemap에도 그대로 남습니다.
피드에서만 빠집니다.
한 가지 더 알아둘 점이 있습니다. git 정보를 켠 사이트에서는 lastBuildDate가 피드에 들어가는 페이지 파일을 마지막으로 건드린 커밋 시각이 됩니다.
빈 사이트에서 오래된 글에 한 줄만 고쳐 커밋했더니 이 값이 그 커밋 시각으로 움직였고 layouts/만 바꾼 커밋에는 움직이지 않았습니다.
이름은 빌드 시각처럼 들리지만 실제로는 항목들의 마지막 수정일입니다.
다른 방법도 해봤는데 부작용이 있었습니다

템플릿을 덮어쓰기 전에 설정만으로 되는 방법도 빈 사이트에서 돌려봤습니다.
홈 _index.md에 date를 적기. lastBuildDate는 돌아옵니다.
다만 0001년 항목은 그대로 남아서 두 번째 문제는 못 풉니다.
개인정보처리방침에 build: list: never 넣기. 피드에서는 빠집니다.
그런데 sitemap에서도 /privacy/가 같이 빠졌습니다. 문서 설명이 “Do not include the page in any page collection"이라
(Build options) 피드만 골라서 빼주는 옵션이 아니었습니다.
검색엔진에 알려야 하는 페이지라면 바라던 결과가 아닐 겁니다.
outputs에서 홈 RSS를 끄고 섹션 RSS를 켜기. home = ['html'], section = ['html', 'rss']로 바꾸면
/posts/index.xml에는 글만 남고 lastBuildDate도 붙습니다.
대신 루트 /index.xml이 생기지 않습니다. 이미 그 주소로 구독한 사람이 있으면 피드 주소가 바뀌는 셈입니다.
결국 피드 주소와 sitemap을 그대로 두고 피드 내용만 바꾸는 방법은 템플릿 덮어쓰기뿐이었습니다.
확인 순서만 남기면

같은 증상이 의심되면 이 순서로 보면 됩니다.
- 배포된 피드를 직접 받아서
grep -c '<lastBuildDate>'결과와0001이 들어간pubDate가 있는지 봅니다 layouts/에rss.xml이나home.rss.xml이 있는지 봅니다. 없으면 내장 템플릿이 쓰이고 있습니다- 설정에
enableGitInfo = true가 있고content/_index.md가 커밋돼 있으면lastBuildDate가 빠지는 조건입니다 date없는 일반 페이지(개인정보처리방침, 소개 페이지 같은 것)가 있으면 홈 피드에 0001년으로 들어갑니다- 내장 템플릿을
layouts/home.rss.xml로 복사하고 위 두 줄을 고칩니다. 고친 뒤에는 피드와 함께 sitemap도 전후를 비교합니다
피드를 직접 열어보기 전까지는 둘 다 몰랐습니다. 빌드가 성공했다는 건 피드 파일이 만들어졌다는 뜻이지, 그 안의 값이 맞다는 뜻은 아니었습니다.