上篇记了从零到本地预览的流程。这篇解决三个真正开始写站之后绕不开的问题:图片怎么 跟文章放在一起、主题模板想微调该改哪、以及怎么把站点发到自己的服务器上。
1. 用 Page Bundle 管理一篇文章的所有资源
Hugo 里一篇文章有两种放法。第一种是上篇那种单个文件 content/posts/hello.md;当文章
需要带图片、附件时,更推荐用 Page Bundle(页面包):把文章改成一个目录,里面放
index.md 和它用到的所有资源。
content/posts/
└── my-post/
├── index.md # 文章正文(文件名必须是 index.md)
├── cover.png # 封面
└── images/
└── shot.png # 正文插图

这样做的好处是:图片和文章是一个整体。正文里直接用相对路径引用即可,Hugo 会自动 找到同目录资源并处理:

封面则写在 front matter 里,PaperMod 主题会把它渲染成文章头图,还会自动生成多尺寸 响应式图片:
cover:
image: "cover.png"
alt: "封面说明"
caption: "图注文字"
小坑:Bundle 目录里正文文件必须叫
index.md(叶子 Bundle)。图片放在 Bundle 内, 既能被 Markdown 相对路径引用,也能被cover.image匹配到。
2. 模板查找顺序:改主题,别动主题
直接去 themes/PaperMod/ 里改模板是下策——下次 git submodule update 升级主题就被
覆盖了。Hugo 的做法是在站点根目录的同名路径放一个文件去覆盖。
查找规则简单说就是:站点自己的 layouts/ 优先,找不到才去 themes/<主题>/layouts/。
所以想改页脚,不用动主题,只要新建:
layouts/
└── partials/
└── footer.html # 覆盖 themes/PaperMod/layouts/partials/footer.html
Hugo 渲染时会优先用你这份,主题升级也不受影响。我这个站就用这个方式加了自定义页脚 (备案信息之类)。几个最常覆盖的位置:
| 想改的东西 | 覆盖路径(放在站点根 layouts/ 下) |
|---|---|
| 页脚 | partials/footer.html |
| 页头额外代码 | partials/extend_head.html |
| 文章正文末尾 | partials/extend_post_content.html |
| 单篇文章模板 | _default/single.html |
3. 配置里的 taxonomy 与菜单
hugo.toml 里显式声明分类法后,tags、categories 这些页面才会按预期生成;菜单也
在这里配:
[taxonomies]
category = "categories"
tag = "tags"
series = "series"
[menu]
[[menu.main]]
name = "文章"
url = "/posts/"
weight = 10
weight 决定菜单从左到右的顺序。改配置不需要重启,hugo server 会自动热加载。
4. 构建:生成纯静态文件
预览没问题后,一条命令产出可部署的静态站点:
hugo --minify

它会把所有 Markdown 渲染、套用模板、压缩 CSS/HTML,结果全部输出到 public/ 目录。
输出末尾的统计表格会告诉你生成了多少页面、处理了多少张图片——这个 public/ 就是
要上传到服务器的全部东西,里面没有任何需要服务器运行的程序。
--minify 会压缩产物;线上构建还建议保证配置里 baseURL 填的是真实域名,否则
RSS、站点地图里的绝对链接会不对。
5. 一键部署到自己的服务器
我的部署方式是:本地打包源码 → 上传 → 服务器上 hugo 构建 → 同步到 Nginx 根目录。
之所以在服务器上构建而不是本地构建,是为了保证环境一致,也省去本地传一大堆产物。
核心脚本长这样:
# 1. 本地打包源码(排除 public 和主题的 .git)
tar czf /tmp/blog-src.tar.gz --exclude='blog/public' blog
# 2. 上传
scp /tmp/blog-src.tar.gz ubuntu@<服务器>:/tmp/
# 3. 服务器上解包并构建
ssh ubuntu@<服务器> "cd ~/blog-src/blog && hugo --minify"
# 4. 备份旧站,再同步到 Nginx 根目录(保护证书续期目录)
ssh ubuntu@<服务器> "sudo cp -a /var/www/blog /var/www/blog.bak.\$(date +%Y%m%d)
&& sudo rsync -a --delete --filter='protect .well-known/' \
~/blog-src/blog/public/ /var/www/blog/"

几个我踩过、值得注意的点:
rsync --delete很危险也很好用:它会让目标目录和源完全一致,删掉的文章也会从 线上消失。所以我在同步前先cp -a备份一份旧站,出问题能秒回滚。- 保护
.well-known/:acme.sh 做 HTTP-01 验证、以及证书续期都依赖/.well-known/acme-challenge/这个路径。用--filter='protect .well-known/'让 rsync 不要删它,否则证书续期会失败(这坑我在 HTTPS 那篇 里详细记过)。 - 文件权限:同步后把属主改成
www-data、目录 755、文件 644,Nginx 才能正常读。
Nginx 那边就是一个最普通的静态站点配置,root 指向 /var/www/blog,
try_files $uri $uri/ =404; 即可,配合上篇提到的 HTTPS 就完整了。
小结
两篇下来,Hugo 的核心心智模型其实很清晰:content/ 里用 Markdown(和 Page Bundle)
描述内容,layouts/ 决定怎么渲染,hugo --minify 把一切烤成 public/ 里的纯静态
文件,最后随便用什么方式把 public/ 发到服务器就行。 理解了"内容 / 模板 / 产物"
这三层分离,剩下的主题定制和部署自动化都只是体力活。
