【前情提要】 主题装好了,菜单配好了,首页也有“人样”了。老周说:“明天写第一篇文章。”小林打开 hugo new,准备大展身手。但他不知道,YAML front matter 的缩进、draft 的坑、_index.md 和 index.md 的区别,正在前面等着他。

一、老周的任务:写一篇博客

早上到工位,老周丢过来一句话:

“博客搭好了,你得往里写东西。今天写一篇,标题自拟,内容不限,但必须包含:一级标题、二级标题、代码块、图片、列表。”

“就这?”

“就这。”老周说,“但你写完得让它在首页显示出来,还得能通过标签和归档找到。你试试。”

小林觉得挺简单。他打开 SSH,连上 Ubuntu,进入站点目录。

cd ~/myblog

然后敲:

hugo new posts/my-first-post.md

输出:

Content "/home/devops/myblog/content/posts/my-first-post.md" created

“生成了。”小林打开文件:

nano content/posts/my-first-post.md

内容:

---
title: "My First Post"
date: 2026-09-13T10:00:00+08:00
draft: true
---

“就一个头?”小林说,“正文呢?”

“你自己写。”老周说,“front matter 是 Hugo 的‘文章头’,里面写元信息。正文在下面,用 Markdown 写。”

小林在 --- 下面加了一段:

# 我的第一篇博客

大家好,我是小林。今天我开始写博客了。

## 为什么写博客

因为老周说,运维要学会记录。

## 今天学了什么

- Hugo 安装
- PaperMod 主题配置
- Nginx 还没学

```bash
hugo version
```

保存,退出,运行:

hugo server

打开浏览器,访问 http://localhost:1313。

首页空白。

“嗯?”小林刷新了一下。还是空白。

他打开 http://localhost:1313/posts/my-first-post/。

输出:

404 page not found

“啥情况?”小林懵了,“我明明写了啊。”

二、draft: true:Hugo 的“草稿箱”

老周端着保温杯走过来,看了一眼屏幕:“你 front matter 里 draft 写的啥?”

小林打开文件:

---
title: "My First Post"
date: 2026-09-13T10:00:00+08:00
draft: true
---

“draft: true 啊。”小林说。

“那不就得了。”老周说,“draft: true 表示这是草稿,Hugo 默认不发布草稿。你 hugo server 不加参数,草稿不显示。”

“那怎么显示?”

“两种方式。”老周竖起两根手指。

方式一:改 draft 为 false。

draft: false

方式二:hugo server 加 -D 参数。

hugo server -D

“-D 表示 --buildDrafts,构建时包含草稿。你本地开发用这个方便,不用每次都改 draft。”

小林改成 draft: false,刷新。

文章出来了。

“记住了。”老周说,“draft: true 是草稿,不发布。draft: false 是正式文章,发布。 你以后写完文章,想先存着不发,就保持 true。想发,就改 false。”

小林在笔记本上写下:

  • draft: true → 草稿,不发布
  • draft: false → 正式,发布
  • hugo server -D → 本地预览包含草稿

三、front matter:文章的“身份证”

“既然讲到 front matter,我就给你讲全。”老周说,“Hugo 的 front matter 有三种格式:YAML、TOML、JSON。你用的是 YAML,也是最常用的。”

“YAML 有啥特点?”

“缩进敏感。”老周说,“YAML 用空格缩进,不能用 Tab。 你复制网上代码的时候,经常混进 Tab,一混就报错。”

小林记下:YAML 只能用空格,不能用 Tab。

“常用的 front matter 字段有这些。”老周在纸上写:

---
title: "文章标题"
date: 2026-09-13T10:00:00+08:00
draft: false
tags: ["Linux", "Hugo"]
categories: ["运维"]
author: "小林"
description: "文章摘要,用于 SEO"
---

“一个一个说。”

  • title:文章标题,显示在页面顶部和列表里。
  • date:发布日期,格式是 YYYY-MM-DDTHH:MM:SS+时区。你写 2026-09-13 也行,Hugo 会补全。
  • draft:草稿标记,true 或 false。
  • tags:标签,数组格式,用 ["标签1", "标签2"]。
  • categories:分类,也是数组。
  • author:作者,会覆盖站点配置里的作者。
  • description:摘要,出现在首页列表和 SEO 元信息里。

“你把这几个填上。”老周说。

小林改成:

---
title: "我的第一篇博客"
date: 2026-09-13T10:00:00+08:00
draft: false
tags: ["Hugo", "博客"]
categories: ["运维"]
author: "小林"
description: "小林的第一篇博客,记录 Hugo 搭建过程。"
---

保存,刷新。文章标题变成“我的第一篇博客”,底部出现“Hugo”“博客”两个标签,以及“运维”分类。

“点标签和分类看看。”

小林点“Hugo”标签,跳转到 /tags/hugo/,里面列出了这篇文章。

“成了。”小林说。

四、Markdown 正文:Hugo 的“写作语言”

“front matter 搞定了,正文你会写吧?”老周问。

“会一点。”小林说,“大学写过 Markdown。”

“那你写给我看看。一级标题、二级标题、代码块、图片、列表,都写一遍。”

小林在 front matter 下面写:

# 我的第一篇博客

大家好,我是小林。今天我开始写博客了。

## 为什么写博客

因为老周说,运维要学会记录。

## 今天学了什么

- Hugo 安装
- PaperMod 主题配置
- Nginx 还没学

## 代码示例

```bash
hugo version
```

## 图片

![Hugo Logo](/images/hugo-logo.png)

保存,刷新。

标题正常,列表正常,代码块正常,图片挂了。

“图片呢?”小林看着那个裂开的图标。

“你图片放哪了?”

“我没放。”

老周叹了口气:“Markdown 里的图片路径,是相对于 static/ 目录的。你写 /images/hugo-logo.png,就得把图片放在 static/images/hugo-logo.png。你没放,当然挂。”

小林把一张图片下载下来,放到 static/images/ 目录:

mkdir -p static/images
cp ~/Downloads/hugo-logo.png static/images/

刷新,图片出来了。

“记住了。”老周说,“Hugo 的静态文件放 static/,Markdown 里用 / 开头的绝对路径引用。 比如 static/images/hugo-logo.png,引用就是 /images/hugo-logo.png。”

小林在笔记本上写下:

  • 图片、CSS、JS 放 static/
  • Markdown 引用路径:/images/xxx.png
  • 不要用 ../ 相对路径,容易乱

“还有一个坑。”老周说,“路径大小写敏感。 你图片叫 Hugo-Logo.png,引用写 /images/hugo-logo.png,Linux 下直接 404。Ubuntu 是大小写敏感的,Mac 默认不敏感。你在 Mac 上写博客,图片能显示,传到 Ubuntu 就挂了。”

小林心里一紧,赶紧把图片名改成全小写。

五、文章目录:_index.md 和 index.md

“文章写完了。”小林说,“但我发现一个问题。”

“啥问题?”

“我在 content/posts/ 下写了文章,但我访问 http://localhost:1313/posts/ 是 404。只有直接访问 http://localhost:1313/posts/my-first-post/ 才行。”

老周笑了:“你发现了一个经典问题。Hugo 里,content/posts/ 是一个‘节(section)’。你想让 posts/ 有一个列表页,得在里面放一个 _index.md。”

“_index.md 和 index.md 啥区别?”

“这是 Hugo 最容易搞混的地方。”老周说,“我给你列一下。”

  • _index.md:表示这个目录是一个“节(section)”,会生成列表页。/posts/ 会显示这个节下所有文章的列表。
  • index.md:表示这个目录是一个“页面(page)”,不会生成列表。/about/ 是一个独立页面,不需要列表。

“所以你访问 /posts/ 是 404,因为 posts/ 下没有 _index.md。”

“那怎么建?”

nano content/posts/_index.md

内容:

---
title: "文章"
date: 2026-09-13T10:00:00+08:00
draft: false
---

保存,刷新。访问 http://localhost:1313/posts/,出现了文章列表。

“成了。”小林说。

“记住:”

  • content/posts/_index.md → posts 节的列表页
  • content/about/index.md → about 独立页面
  • content/about.md → 也是独立页面,但和 about/index.md 只能选一个

“再教你一个技巧。”老周说,“页面(index.md)可以有自己的资源目录。 比如 content/about/index.md 旁边可以放 content/about/photo.jpg,Markdown 里用 photo.jpg 直接引用,不用放 static/。这叫 Page Bundle,你以后写图文并茂的文章可以用。”

小林记下:列表页用 _index.md,独立页面用 index.md。

六、排错挑战:文章不显示

下午,小林想再写一篇文章,但写完 hugo server 之后,首页还是不显示。

他检查了:

  • draft: false ✓
  • date 是当前日期 ✓
  • 文件在 content/posts/ 下 ✓

但文章就是不显示。

他打开 hugo server 的输出,发现一行黄色警告:

WARN  found no layout file for "html" for kind "page": You should create a template file which matches Hugo Layouts Lookup Rules for this content.

“layout 是啥?”小林不懂。

他喊来老周。

老周看了一眼:“你文件名是不是带空格了?”

小林检查:

ls content/posts/

输出:

my first post.md
my-first-post.md

“你第一个文件名带了空格。”老周说,“Hugo 对文件名有要求,尽量用连字符 -,别用空格、中文、大写字母。”

“那我改成 my-first-post.md?”

“对。”老周说,“文件名只用小写字母、数字、连字符。 这是 URL 的一部分,大写和空格会被转义,很丑,还容易出问题。”

小林改名:

mv "content/posts/my first post.md" content/posts/my-first-post.md

刷新,文章出来了。

排错思路总结:

  1. 文章不显示,先检查 draft 是否为 false。
  2. 检查 date 是否合法(不能是未来日期,除非 --buildFuture)。
  3. 检查文件是否在 content/ 目录下。
  4. 检查文件名是否包含空格、大写、中文——建议全小写、连字符。
  5. 如果 hugo server 输出 found no layout file,检查主题是否完整、theme 配置是否正确。
  6. 最后检查 hugo.toml 里是否 disableKinds = ["page"] 之类的配置。

“记住,”老周说,“Hugo 的文件名,就是 URL。 你文件名写 My First Post.md,URL 就是 /posts/my-first-post/(转义后)。你写 my-first-post.md,URL 就是 /posts/my-first-post/。干净、可读、不出错。”

七、练习

  1. 练习一:用 hugo new posts/my-first-post.md 创建文章,修改 front matter:title 为“我的第一篇博客”,draft 为 false,tags 为 ["Hugo", "博客"],categories 为 ["运维"]。在正文里写一级标题、二级标题、列表、代码块。运行 hugo server,确认首页显示文章,标签和分类可点击。
  2. 练习二:在 static/images/ 下放一张图片(可以从网上下载),在文章中用 ![图片](/images/图片名.png) 引用。确认 hugo server 渲染后图片显示。
  3. 练习三:创建 content/posts/_index.md,设置 title: "文章"。访问 http://localhost:1313/posts/,确认出现文章列表。然后创建 content/about/index.md,设置 title: "关于",写一段自我介绍,访问 http://localhost:1313/about/,确认页面显示。

参考答案

练习一:

hugo new posts/my-first-post.md
nano content/posts/my-first-post.md

front matter:

---
title: "我的第一篇博客"
date: 2026-09-13T10:00:00+08:00
draft: false
tags: ["Hugo", "博客"]
categories: ["运维"]
---

正文:

# 我的第一篇博客

大家好,我是小林。

## 今天学了什么

- Hugo 安装
- PaperMod 主题配置

## 代码示例

```bash
hugo version
```
hugo server

访问 http://localhost:1313,确认文章显示。点击“Hugo”标签,跳转到 /tags/hugo/。

练习二:

mkdir -p static/images
cp ~/Downloads/hugo-logo.png static/images/

文章中添加:

![Hugo Logo](/images/hugo-logo.png)

刷新,图片显示。

练习三:

nano content/posts/_index.md

内容:

---
title: "文章"
date: 2026-09-13T10:00:00+08:00
draft: false
---
mkdir -p content/about
nano content/about/index.md

内容:

---
title: "关于"
date: 2026-09-13T10:00:00+08:00
draft: false
---
我是小林,一名刚入行的运维工程师。

访问 http://localhost:1313/posts/ 和 http://localhost:1313/about/,确认页面正常。

八、下集预告

文章写好了,标签配好了,列表页也有了。老周说:“明天让博客有点‘人样’,加图片、代码块、短代码。”小林打开 PaperMod 文档,准备大干一场。但他不知道,Hugo 的 shortcode、代码高亮、TOC,每一个都有细节。

下一集:《让博客有点“人样”:图片、代码块和短代码》