Hugo 静态站实战:从零搭建到部署上线的完整流程

纯文字博客,为什么我建议试试静态站

很多站长和我一样,最早是从 Typecho、WordPress 这类动态程序入门的,一个 PHP 环境加一个数据库就能把站跑起来,后台写文章也方便。但如果你主要写技术文章、教程笔记,页面没有评论刚需、不需要会员系统,那么每次访问都要经过 PHP 解析加数据库查询,其实是在浪费服务器资源,还要天天担心程序漏洞被打。静态站生成器的思路完全反过来:文章在本地就编译成纯 HTML,服务器上只需要一个 Nginx 就能跑,没有数据库可以被打爆,不存在 PHP 执行漏洞,访问速度还快得离谱,配合 CDN 几乎可以做到秒开。Hugo 是这类工具里构建最快、上手最容易的一个,这篇记录我用 Hugo 从零搭站、写文章、部署上线的完整过程,以及静态站常见短板的补齐方案。

一、安装 Hugo

Hugo 是 Go 语言写的单文件程序,没有一堆运行时依赖,安装最省事的方式是直接下载编译好的二进制。到 GitHub 的 gohugoio/hugo 仓库 Releases 页面,找对应系统的安装包,Linux 服务器就选 deb 包:

# 版本号按需替换,建议用最新的稳定版
wget https://github.com/gohugoio/hugo/releases/download/v0.128.0/hugo_extended_0.128.0_linux-amd64.deb
dpkg -i hugo_extended_0.128.0_linux-amd64.deb
hugo version

务必选 extended 版本,它内置了 SCSS 编译支持,很多主题的主题样式都是用 SCSS 写的,普通版跑这些主题会直接报错。Debian 的 apt 源里也有 hugo,但版本通常落后好几个大版本,和最新主题的兼容性差,不推荐图省事用 apt 装。装完跑 hugo version 确认一下版本号,顺便验证二进制能正常运行。

二、创建站点与基础配置

新建站点只需要一条命令,目录名就是你的项目名:

hugo new site myblog

cd myblog

生成出来的目录结构非常清晰:content 放 Markdown 文章,themes 放主题,static 放图片、favicon 等原样拷贝的资源,public 是构建输出目录(初始不存在),hugo.toml 是站点配置文件。Hugo 0.110 之后的默认配置文件名是 hugo.toml,网上很多老教程写的 config.toml 也能用,别被搞糊涂。最基础的配置项长这样:

baseURL = "https://blog.example.com/"
title = "我的技术博客"
languageCode = "zh-cn"
theme = "papermod"
paginate = 10

[params]
description = "个人技术笔记与建站经验分享"

baseURL 一定要填最终上线的域名,它会影响站点地图、RSS、canonical 链接的生成,填错了后面改要全站重新构建。接着安装主题,以目前最流行的 PaperMod 为例:

git clone https://github.com/adityatelange/hugo-PaperMod themes/papermod

主题目录名要和 hugo.toml 里的 theme 字段一致,然后跑 hugo server 就能在本地看到默认效果了。主题的详细配置项写在各主题自己的文档里,PaperMod 的开关基本都集中在 hugo.toml 的 [params] 段,比如首页简介、社交链接、文章列表要不要显示摘要、代码块要不要高亮等。挑主题的原则是选文档全、更新勤的,别选几年不维护的,Hugo 版本迭代快,老主题很容易出兼容问题。

三、写文章:front matter 是核心

新建文章不要自己手动创建文件,规范姿势是让 Hugo 用 archetype 模板生成,保证格式统一:

hugo new posts/nginx-keepalive-config.md

生成的文件头部有一段 YAML 格式的 front matter,用上下各三个短横线包裹,文章的所有元信息都写在这里,这是 Hugo 和普通 Markdown 最大的区别:

---
title: "Nginx keepalive 配置详解"
date: 2026-09-03T10:00:00+08:00
draft: true
tags: ["Nginx", "性能优化"]
categories: ["运维"]
description: "keepalive 的作用原理与 Nginx 配置实践"
---

title 是文章标题,date 建议写成带时区的完整格式,Hugo 按这个时间排序文章列表,只写日期不写时间有时区坑。draft 为 true 表示草稿,构建时默认不会输出到 public,写完了改成 false 或者删掉这行。description 会用在页面描述和 SEO 的 meta 标签里,养成填的习惯对收录有好处。正文用标准 Markdown 写,Hugo 内置代码高亮,写技术文章的体验比任何富文本编辑器都舒服:三个反引号包起来的代码块,构建时会自动加上高亮和行号,语言写在反引号后面就行。文章配图放 static 目录下,用相对路径引用,纯文字博客也可以暂时完全不用图。

四、本地预览与构建

写作过程中用下面这条命令实时预览,这是 Hugo 工作流里最爽的一环:

hugo server -D

默认监听 localhost:1313,浏览器打开就能边写边看,保存文件后页面自动刷新,不用手动刷新浏览器。加 -D 参数是为了让草稿也显示出来,否则 draft 状态的文章在预览里看不到,正式构建时要去掉。写完一批文章,正式构建:

hugo --minify

构建产物全部输出到 public 目录,--minify 会压缩 HTML 里的空白字符。构建速度是 Hugo 的招牌,几百篇文章也是秒级完成,这也是我选它而不是 Hexo 的主要原因——Hexo 文章多了之后构建会明显变慢,Hugo 是 Go 写的,处理几千篇文章依然毫秒级,内容多了也不慌。构建完先在本地开个静态服务器检查一遍:

cd public && python3 -m http.server 8080

浏览器访问服务器 IP 加端口,把每个栏目的链接都点一遍,重点看有没有 404。本地检查过关再部署,比推到线上发现问题再回滚省事得多。

五、部署上线:rsync 推送到 VPS

静态站部署的本质就是把 public 目录的内容同步到服务器。最直接的方式是本地构建完 rsync 推上去,增量同步、支持断点续传,几百兆的站点也就几秒钟:

rsync -avz --delete public/ root@服务器IP:/var/www/myblog/

--delete 参数保证服务器上多出来的旧文件会被清理,避免删掉的文章还留在线上。注意 public/ 后面的斜杠不能丢,带斜杠是同步目录内容,不带斜杠会多套一层目录。服务器上 Nginx 的配置非常简单,毕竟没有 PHP、没有数据库,就一个静态文件服务器:

server {

listen 80;
server_name blog.example.com;
root /var/www/myblog;
index index.html;

location / {
    try_files $uri $uri/ =404;
}

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000";
}

}

try_files 先找文件再找目录,都找不到就返回 404,比默认的 index 行为严谨。静态资源的缓存头一定要加,Hugo 构建时会给每个静态文件生成内容指纹(文件名里带 hash),文件名变了内容才变,所以可以放心大胆地缓存 30 天。HTML 页面本身不要长缓存,否则更新文章后用户看到的是旧页面,配合 CDN 使用时尤其要注意页面级缓存的刷新策略。上线后别忘了配 HTTPS,静态站没有后端程序,配证书最简单,一条 certbot 命令搞定,这里就不展开了。

六、进阶:GitHub Actions 自动发布

手动 rsync 适合一个人折腾,但如果想享受"git push 即发布"的体验,可以接 GitHub Actions:本地只管写文章推代码,服务器更新全自动。思路是先在服务器上生成部署密钥,公钥加进仓库的 Secrets,私钥放在服务器,然后写一个工作流文件 .github/workflows/deploy.yml:

name: Deploy
on:
push:

branches: [main]

jobs:
build:

runs-on: ubuntu-latest
steps:
  - uses: actions/checkout@v4
  - uses: peaceiris/actions-hugo@v3
    with:
      hugo-version: "0.128.0"
      extended: true
  - run: hugo --minify
  - uses: peaceiris/actions-rsync@v1
    with:
      rsync_options: "-avz --delete"
      src: "public/"
      dest: "root@服务器IP:/var/www/myblog"
      ssh_private_key: ${{ secrets.DEPLOY_KEY }}</pre><p>以后写完文章 git add、commit、push 三连,等一两分钟服务器就更新了,出差用手机改个错别字也能直接推。这个方案对个人站长还有个额外好处:文章源码有 GitHub 当异地备份,服务器挂了数据也不丢。不想自己维护服务器的,还可以把 public 目录直接部署到 Cloudflare Pages 或 GitHub Pages 这类免费静态托管,Hugo 官方文档有现成的部署指南,跟着点几下就行,连服务器钱都省了。</p><h2>七、静态站缺的配套功能怎么补</h2><p>静态站最大的争议是"没后台、没评论",其实都有成熟的免费方案。评论用 giscus 或者 waline:giscus 基于 GitHub Discussions,读者用 GitHub 账号就能留言,评论数据存在你的仓库里;waline 可以部署在 Vercel 等免费平台,支持匿名评论和邮件通知。两者都是往页面里嵌一段脚本的事,主题基本都内置了开关。站内搜索可以用 Hugo 自带的 index 配合 Pagefind 或者 lunr,构建时生成索引文件,纯前端检索,不需要服务端。统计用我之前介绍过的自建 Umami,或者干脆看 CDN 的日志。表单类需求(比如留言板、投稿)确实不太适合纯静态站,真有需求就保留一个动态程序专门处理,或者用 Formspree 这类第三方表单服务把结果转成邮件。想清楚自己到底需要哪些功能再迁移,就不会被"没后台"吓退。</p><h2>八、从动态站迁移的几个注意点</h2><p>如果你打算把现有的 Typecho 或 WordPress 站迁到 Hugo,有几点经验值得记下来。第一是 URL 结构,动态站的文章链接一般是 /archives/123.html 这种带 ID 的格式,而 Hugo 默认是 /posts/文章名/,直接换会导致老链接全部失效,收录和外部链接全部归零。迁移时在 hugo.toml 里配好 permalinks,让新站链接保持和旧站一致:</p><pre>[permalinks]

posts = "/archives/:slug.html"

slug 在每篇文章的 front matter 里显式指定为旧站的文章名,保证一一对应。实在对不上的,在 Nginx 里写几条 rewrite 做 301 跳转,把流量导到新地址。第二是内容格式转换,动态站导出的一般是 HTML,要转成 Markdown,可以用 pandoc 批量转换,但代码块、图片路径要人工抽查,转完用 hugo server 逐篇预览一遍最稳妥。第三是评论数据,如果旧站积累了不少评论,要么导入到新评论系统,要么至少保留一份存档页面,别让老读者的留言凭空消失。第四是广告位,原来在文章里插的广告代码要重新规划位置,静态站插广告比动态站还方便,直接在主题模板里加就行,但注意别破坏页面结构。想清楚这四点,迁移过程基本不会翻车。

最后总结一下适合场景:内容以文字为主、更新频率稳定、不需要复杂交互的博客,静态站是性价比极高的选择——速度快、开销低、安全省心,还能把全部精力放在写作本身。等哪天你需要会员、需要复杂交互了,再迁回动态程序也不迟,文章都是 Markdown,数据永远在你手里,这才是个人站长最踏实的底气。

Last modification:September 3rd, 2026 at 07:58 am

Leave a Comment