这篇文章记录一种简单、可维护的静态博客部署方式:用 Hugo 生成静态页面,用 Caddy 对外提供访问和自动 HTTPS。
示例中统一使用 blog.example.com 作为占位域名。实际部署时,把它替换成自己的域名即可。
架构思路
Hugo 和 Caddy 各自负责一件事:
- Hugo:把 Markdown、主题和配置生成成静态 HTML/CSS/JS。
- Caddy:监听
80/443,对外提供静态文件服务,并自动申请和续签 HTTPS 证书。
推荐目录结构:
/opt/blog # Hugo 源码目录:配置、文章、主题
/var/www/blog # Hugo 生成后的静态文件目录,Caddy 对外读取这里
/etc/caddy # Caddy 配置目录
日常只编辑 /opt/blog。/var/www/blog 是构建产物,不手动修改。
权限模型
一个容易踩坑的点是 /var/www/blog 的权限。
Caddy 只是读取静态文件,不需要拥有发布目录。因此推荐:
/opt/blog归日常登录用户所有。/var/www/blog也归日常登录用户所有。- Caddy 只需要通过普通文件权限读取
/var/www/blog。
这样每次发布时,Hugo 可以直接写入 /var/www/blog,不需要反复 chown。
假设日常登录用户是 deploy,可以这样设置:
sudo mkdir -p /opt/blog /var/www/blog
sudo chown -R deploy:deploy /opt/blog /var/www/blog
find /var/www/blog -type d -exec chmod 755 {} \;
find /var/www/blog -type f -exec chmod 644 {} \;
如果是个人服务器,也可以直接把 deploy 替换成自己的用户名。
前置条件
部署前需要准备:
- 一台 Linux 服务器。
- 一个域名,例如
blog.example.com。 - 域名 A 记录已经指向服务器公网 IP。
- 服务器公网可访问
80和443。 - 有 sudo 权限。
Caddy 自动 HTTPS 依赖域名解析和端口可达。如果域名还没解析好,Caddy 证书申请会失败。
安装 Caddy
Debian/Ubuntu 上可以先用系统包安装:
sudo apt-get update
sudo apt-get install -y caddy git
确认 Caddy 正常:
caddy version
systemctl status caddy
Caddy 会在后台自动管理 HTTPS 证书。正常情况下,不需要手动运行 certbot 或单独维护证书续签任务。
安装 Hugo
发行版仓库里的 Hugo 可能偏旧,容易和新主题不兼容。建议安装 Hugo 官方 extended 版本。
到 Hugo 的 release 页面选择最新的 hugo_extended_*_linux-amd64.deb,然后安装:
curl -L -o /tmp/hugo.deb \
https://github.com/gohugoio/hugo/releases/download/vX.Y.Z/hugo_extended_X.Y.Z_linux-amd64.deb
sudo apt-get install -y /tmp/hugo.deb
rm -f /tmp/hugo.deb
把 X.Y.Z 替换成实际版本号。
确认版本:
hugo version
如果使用 PaperMod 这类现代主题,Hugo 版本建议保持较新。
创建 Hugo 站点
创建目录并进入源码目录:
sudo mkdir -p /opt/blog /var/www/blog
sudo chown -R "$USER:$USER" /opt/blog /var/www/blog
hugo new site /opt/blog --force
cd /opt/blog
安装主题。这里以 PaperMod 为例:
git clone --depth 1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
配置 Hugo
编辑 /opt/blog/hugo.toml:
baseURL = "https://blog.example.com/"
locale = "zh-cn"
title = "My Blog"
theme = "PaperMod"
timeZone = "Asia/Shanghai"
[pagination]
pagerSize = 10
[params]
env = "production"
defaultTheme = "auto"
ShowReadingTime = true
ShowPostNavLinks = true
ShowBreadCrumbs = true
ShowCodeCopyButtons = true
ShowToc = true
[params.homeInfoParams]
Title = "My Blog"
Content = "记录技术、生活和想法。"
[[menu.main]]
identifier = "posts"
name = "文章"
url = "/posts/"
weight = 10
[outputs]
home = ["HTML", "RSS", "JSON"]
最重要的是 baseURL,它应该和实际访问域名一致。
新增第一篇文章
创建文章:
cd /opt/blog
hugo new posts/hello-hugo.md
编辑 content/posts/hello-hugo.md:
---
title: "Hello Hugo"
date: 2026-05-26T17:00:00+08:00
draft: false
---
第一篇文章。这个博客由 Hugo 生成,并由 Caddy 托管。
注意 draft: false。如果还是 draft: true,默认构建不会发布这篇文章。
构建并发布
执行:
hugo -s /opt/blog -d /var/www/blog --cleanDestinationDir
这会把 /opt/blog 里的源码构建到 /var/www/blog。
因为 /var/www/blog 归日常用户所有,所以不需要每次发布后再 chown 给 Caddy。Caddy 能读这些文件即可。
配置 Caddy
编辑 /etc/caddy/Caddyfile:
blog.example.com {
root * /var/www/blog
encode gzip zstd
file_server
}
格式化并重载配置:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo systemctl reload caddy
如果 DNS 和防火墙都正确,访问:
https://blog.example.com
Caddy 会自动把 HTTP 跳转到 HTTPS,并自动维护证书。
做一个一键发布命令
为了减少日常操作,可以创建一个发布脚本:
sudo tee /usr/local/bin/publish-blog >/dev/null <<'EOF'
#!/usr/bin/env bash
cd /opt/blog || exit 1
hugo -s /opt/blog -d /var/www/blog --cleanDestinationDir
EOF
sudo chmod 755 /usr/local/bin/publish-blog
之后发布只需要:
publish-blog
如果只是新增或修改文章,不需要重载 Caddy。Caddy 服务的是静态目录,文件更新后会直接生效。
只有修改 /etc/caddy/Caddyfile 时,才需要:
sudo systemctl reload caddy
日常写作流程
新增文章:
cd /opt/blog
hugo new posts/my-new-post.md
nano content/posts/my-new-post.md
publish-blog
修改文章:
cd /opt/blog
nano content/posts/my-new-post.md
publish-blog
删除文章:
cd /opt/blog
rm content/posts/my-new-post.md
publish-blog
核心原则是:
- 文章源文件放在
/opt/blog/content/posts/。 - 发布产物生成到
/var/www/blog。 - 不直接编辑
/var/www/blog。
常见问题
Caddy 申请证书失败
优先检查:
- 域名是否解析到服务器公网 IP。
80/443是否对公网开放。- 是否有其他进程占用了
80/443。 - Caddyfile 里的域名是否写错。
查看日志:
journalctl -u caddy --no-pager -n 100
Hugo 构建失败
常见原因是 Hugo 版本太旧,主题要求更高版本。
检查版本:
hugo version
如果版本过旧,升级到 Hugo 官方 extended 版本。
修改文章后网页没变化
确认是否重新发布:
publish-blog
也可以确认 Caddy 服务的目录:
grep -n "root" /etc/caddy/Caddyfile
root 后面的目录应该是 Hugo 的输出目录,例如 /var/www/blog。
发布时遇到权限错误
如果看到 Hugo 无法写入 /var/www/blog,通常是目录归属不对。修正为日常发布用户即可:
sudo chown -R "$USER:$USER" /var/www/blog
find /var/www/blog -type d -exec chmod 755 {} \;
find /var/www/blog -type f -exec chmod 644 {} \;
Caddy 不需要拥有这些文件,只要能读取即可。
总结
这套部署方式的核心是:
/opt/blog保存 Hugo 源码和文章。/var/www/blog保存 Hugo 生成后的静态网站。/var/www/blog归发布用户所有,Caddy 只读。- Caddy 负责 HTTP/HTTPS 和静态文件服务。
- 日常写作只需要编辑 Markdown,然后运行
publish-blog。
这样配置后,个人博客的维护成本很低:写文章、构建、发布,HTTPS 交给 Caddy 自动处理。