春江暮客

春江暮客的个人学习分享网站

Hugo 文章不显示怎么办:排查草稿、发布时间、过期时间与输出路径

2026-10-26 技术
Hugo 文章不显示怎么办:排查草稿、发布时间、过期时间与输出路径

Hugo 返回构建成功,新文章却找不到:可能是草稿,也可能是服务器时区让发布时间落在未来,还可能是页面已生成但地址猜错了。本文面向初级到中级读者,先查看 Hugo 实际识别的元数据,再用全新目录区分“未生成”和“未展示”。

本文衔接发布前站内链接检查。整个排查过程只写临时目录,无需修改主题或服务器配置。

1. 方法:先区分三种情况

  1. 发布条件未通过:检查 draft、生效的 publishDate 和 expiryDate。
  2. 没有页面文件:在新输出目录中查找 HTML,再检查构建选项和模板。
  3. 文件存在但首页没有:核对主题的文章类型筛选、分页和页面集合,而不是反复改发布时间。

hugo list all 包含草稿、未来和过期内容;hugo list published 排除这三类。它们反映内容资格,不是线上 HTTP 可用性的证明。

2. 环境准备

需要已有 Hugo 可执行文件及 Python 3.9+,无需第三方 Python 包。本文实验在 Linux、Hugo 0.164.0 和 Python 3.11.4 下验证。先在项目根目录运行:

hugo version
python3 --version
hugo list all --help
hugo list published --help

下面使用 bash/zsh。把 hugo_config 改成真实发布流程所用的配置文件;例如本网站使用 hugotest.toml。同时保持 --environment 等选项与正式构建一致,避免诊断另一套配置。

3. 列出被排除的文章

在同一个终端执行:

hugo_config=hugo.toml
audit_dir=$(mktemp -d)
trap 'rm -rf -- "$audit_dir"' EXIT
hugo list all --config "$hugo_config" > "$audit_dir/all.csv"
hugo list published --config "$hugo_config" > "$audit_dir/published.csv"
python3 - "$audit_dir" <<'PY_AUDIT'
import csv
from pathlib import Path
import sys

root = Path(sys.argv[1])
with (root / "published.csv").open(encoding="utf-8", newline="") as stream:
    published = {row["path"] for row in csv.DictReader(stream)}
with (root / "all.csv").open(encoding="utf-8", newline="") as stream:
    for row in csv.DictReader(stream):
        if row["kind"] == "page" and row["path"] not in published:
            print(row["path"])
            for key in ("draft", "date", "publishDate", "expiryDate", "permalink"):
                print(f"  {key}: {row[key]}")
PY_AUDIT

输出会显示被排除文章的路径及关键字段。例如:

content/posts/new-guide.md
  draft: false
  date: 2024-01-01T00:00:00Z
  publishDate: 2030-01-01T00:00:00Z
  expiryDate: 0001-01-01T00:00:00Z
  permalink: https://example.org/new-guide/

这是示意输出;你的文件名和域名会不同。0001-01-01... 是未设置过期时间时可能出现的零值,不表示文章在公元 1 年已经过期。脚本用 csv.DictReader 读取列,避免标题中的逗号破坏字段对齐;没有输出只表示没有找到上述差集。

日期解析配置决定生效值:默认发布日先找 publishdate、pubdate、published,最后回退到 date。所以只改 date,不能覆盖另一个仍在未来的 pubdate;项目自定义了 [frontmatter] 时还要检查它。读取列表中的结果,比只看 Markdown 某一行更可靠。

4. 根据原因修正,再构建到新目录

现象 具体处理
draft: true 文章确实准备发布后改成 draft: false。
publishDate 晚于当前时间 核对 date -Is 和日期的时区;立即发布的文章应修正实际生效的发布字段。
有效 expiryDate 已经过期 核实过期策略,继续保留的文章删除误设的过期字段或改为合适时间。
资格正常,但访问旧地址 404 按列表中的 permalink 查输出,核对 front matter 的 url 和语言路径。

pubdate 是 publishDate 的别名。同一文章选择一种写法;日期带上 +08:00 或 Z 等偏移量,避免按错误时区理解。

修正自己的目标文章后运行普通构建:

preview_dir=$(mktemp -d)
hugo --config "$hugo_config" --destination "$preview_dir"
find "$preview_dir" -type f -name '*.html' | head -n 20
printf 'Preview directory: %s\n' "$preview_dir"

按实际地址查找文件:根路径站点的 /guide.html 通常对应 guide.html,/guide/ 对应 guide/index.html。多语言或子路径部署要结合配置核对。Hugo URL 文档说明了 url 如何覆盖默认路径。

需要对照隐藏内容的渲染结果时,再建一个独立目录:

diagnostic_dir=$(mktemp -d)
hugo --config "$hugo_config" --buildDrafts --buildFuture --buildExpired \
  --destination "$diagnostic_dir"
printf 'Diagnostic directory: %s\n' "$diagnostic_dir"

这些构建参数仅用于本地诊断。只在诊断目录出现的页面说明发布条件需要处理;不要把该目录同步到线上。原有输出目录可能留着上一轮生成的文件,不能用来证明本轮确实渲染了文章。

若普通构建仍缺 HTML,检查日志和模板;同时查看文章、父级 cascade 中的 build.render 与 build.list。render: never 不生成页面,list: never 会使页面退出集合。若 HTML 存在但首页缺文章,还要检查主题是否只列出特定 type 或 section,以及分页位置。

检查完只删除本次创建的两个目录:

rm -rf -- "$preview_dir" "$diagnostic_dir"

5. 验证:可重复的四文章实验

将下面代码保存为 test_hugo_visibility.py。它在临时站点创建四个测试文件,固定 Hugo 的诊断时钟,然后检查列表及五种构建组合;不读取或改动你的站点文章。程序结束时自动清理临时站点。

"""Verify Hugo visibility rules in a temporary site. Python 3.9+."""
import csv
import io
from pathlib import Path
import subprocess
from tempfile import TemporaryDirectory

CLOCK = "2026-01-01T00:00:00Z"


def run_hugo(root, *args):
    result = subprocess.run(
        ["hugo", "--source", str(root), "--clock", CLOCK, *args],
        check=True, capture_output=True, text=True, encoding="utf-8", timeout=60,
    )
    return result.stdout


with TemporaryDirectory(prefix="hugo-visibility-") as work:
    root = Path(work)
    (root / "content").mkdir()
    layouts = root / "layouts" / "_default"
    layouts.mkdir(parents=True)
    (root / "hugo.toml").write_text(
        'baseURL = "https://example.org/"\n'
        'disableKinds = ["home", "taxonomy", "term", "RSS", "sitemap"]\n',
        encoding="utf-8",
    )
    (layouts / "single.html").write_text(
        "<!doctype html><title>{{ .Title }}</title>{{ .Content }}",
        encoding="utf-8",
    )
    cases = {
        "visible": "",
        "draft": "draft: true\n",
        "future": "publishDate: 2030-01-01T00:00:00Z\n",
        "expired": "expiryDate: 2020-01-01T00:00:00Z\n",
    }
    for name, extra in cases.items():
        (root / "content" / f"{name}.md").write_text(
            f"---\ntitle: {name}\ndate: 2024-01-01T00:00:00Z\n"
            f"url: /{name}.html\n{extra}---\n\nFixture: {name}.\n",
            encoding="utf-8",
        )
    all_rows = list(csv.DictReader(io.StringIO(run_hugo(root, "list", "all"))))
    published = list(csv.DictReader(io.StringIO(run_hugo(root, "list", "published"))))
    assert {row["title"] for row in all_rows} == set(cases)
    assert {row["title"] for row in published} == {"visible"}
    builds = [
        ([], {"visible"}),
        (["--buildDrafts"], {"visible", "draft"}),
        (["--buildFuture"], {"visible", "future"}),
        (["--buildExpired"], {"visible", "expired"}),
        (["--buildDrafts", "--buildFuture", "--buildExpired"], set(cases)),
    ]
    for index, (flags, expected) in enumerate(builds):
        destination = root / f"output-{index}"
        run_hugo(root, "--destination", str(destination), *flags)
        actual = {path.stem for path in destination.glob("*.html")}
        assert actual == expected, (flags, actual, expected)
        for name in expected:
            assert f"Fixture: {name}." in (destination / f"{name}.html").read_text()
    print("PASS: inventories and 5 isolated builds")

运行:

python3 test_hugo_visibility.py

预期输出 PASS: inventories and 5 isolated builds。固定时间仅用于实验,正式构建应使用实际时间;每种组合输出到新目录,并检查 HTML 正文。断言失败或命令超时会返回非零状态,请先查看报错,不要把这些开关直接加进发布脚本。

6. 常见问题

  • 两个列表都没有目标文件:检查配置里的内容目录、语言、文件扩展名及排除规则;确认文件真的在本次构建读取的位置。
  • 预览能看,部署后 404:核对部署目录、域名和实际响应内容,按 curl 网站检查教程 排查。
  • 本地正常,自动构建缺文章:在构建日志记录 hugo version、配置文件和当前时间,对照 Hugo 自动部署教程 检查运行环境。

7. 总结

先比较内容列表,再检查全新目录中的 HTML,可以把发布条件、输出地址和首页展示问题分开定位。修正生效的元数据后,用普通构建和实际 HTTP 响应验证结果。保留自动化实验,升级 Hugo 或调整发布流程时就能重新检查这些行为。

标签

1024 12306 ablang adsense agents.md ai ai-agent ai-agents ai-seo algorithm amp antibodies apparmor argparse automation backup bash batch-processing binarycif bioinformatics biopython blockchain boltz bootstrapping boxes bubblewrap c-index cca cdn chatgpt checkpoint cli cloudflare codex cofoldarena concurrency configuration copy cprofile cpu监控 cron csv cuda curl data-leakage data-processing data-quality data-validation datascience datavisualization datetime debugging decimal deployment desktop-app devtools disk-space disown dns docker dovecot download electron encoding esm esm2 esm3 esmc esmfold2 faceswap fasta fastmcp faulthandler ffmpeg file-descriptors file-io flashppi flask folium frontend game generator git github-actions google google-research grep gzip harness hls html http hugo imports indexnow javascript jev json jsonl just k-means kaggle langfuse leecode linux list litellm llm llms.txt logging logs lollipop lsof m3u8 machine-learning macos manacher matplotlib mcp memory mirror mmcif model-evaluation monitoring mp3 mp4 mpnn multiomics mutation mysql nanobert nanobodies networkx nginx normalize numpy ollama omegatherm pandas password pathlib pdb pep-723 performance phaser pillow pip postfix preprocessing print protein-alignment protein-design protein-embeddings protein-interactions protein-language-models protein-stability protein-structure proxy pydantic pyecharts pyqt python python3 r raincloud reproducibility requests reservoir-sampling rfoptimization rg ripgrep rosettafold3 roundcube rrsi rsi rsync s-tui sampling scale scikit-learn scrapy screen seaborn security selenium seo sequence-identity sha256 shell signals sklearn socket solana somaticsignatures spl sqlite ssh standardize static-site subprocess symlink sysadmin system-one tarfile tcp tempfile tensorflow tkinter tls toml tracemalloc tron tronpy troubleshooting turtle typesafe-ai ubuntu unicode urllib usdt utf-8 uv vhhbert vite webp wordcloud wordpress workflow yaml zipfile zoneinfo 后台 寓言 概率 经济 贸易 迅雷解析 钱包

友情链接

其它