使用 Hugo + GitHub + Cloudflare 搭建个人博客

2026年8月5日 周三 1039 字 5 分钟 -- 博客指南 Hugo, GitHub, Cloudflare, 教程, 博客搭建

前言#

搭建一个个人博客,我选择了以下技术栈:

组件 选型 作用
静态生成器 Hugo 将 Markdown 转为静态网页
代码托管 GitHub 存储博客源码和内容
部署平台 Cloudflare Pages 自动构建并部署到全球 CDN
内容管理 Decap CMS 提供可视化的文章编辑界面

这套方案的优势:完全免费、全球加速、自动部署、支持可视化编辑。


一、安装 Hugo#

macOS#

brew install hugo

Windows#

winget install Hugo.Hugo.Extended

验证安装#

hugo version
# hugo v0.164.0+extended ...

建议安装 extended 版本,支持 SCSS 编译。


二、初始化 Hugo 项目#

创建站点#

hugo new site my-blog
cd my-blog

安装主题#

这里以 hugo-book 主题为例,使用 git submodule 方式安装:

git init
git submodule add https://github.com/alex-shpak/hugo-book themes/hugo-book

基础配置#

编辑 hugo.toml:

baseURL = "https://your-domain.com/"
locale = "zh-cn"
defaultContentLanguage = "zh"
theme = "hugo-book"
paginate = 10

[params]
  BookTheme = "auto"        # 自动切换明暗主题
  BookToC = true            # 显示目录
  BookSection = "posts"     # 文章所在 section
  BookSearch = true         # 启用搜索
  BookDateFormat = "Jan 2, 2006"

[markup]
  [markup.highlight]
    codeFences = true
    style = "dracula"       # 代码高亮主题

  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true          # 允许 HTML

[menu]
  [[menu.after]]
    identifier = "posts"
    name = "文章"
    url = "/posts/"
    weight = 20

创建第一篇文章#

hugo new content posts/hello-world.md

编辑生成的文件:

---
title: "你好世界"
date: 2026-01-01T10:00:00+08:00
draft: false
tags: ["介绍"]
categories: ["通用"]
summary: "我的第一篇博客文章"
---

## 欢迎来到我的博客

这是我的第一篇博客文章!

本地预览#

hugo server -D
# 访问 http://localhost:1313

三、项目目录结构#

搭建完成后,项目结构如下:

my-blog/
├── archetypes/          # 文章模板
│   └── default.md
├── assets/              # 需要处理的静态资源(SCSS、JS)
├── content/             # 内容文件
│   ├── posts/           # 文章
│   │   └── hello-world.md
│   └── menu/            # 菜单配置
├── data/                # 数据文件
├── i18n/                # 国际化翻译
├── layouts/             # 自定义布局模板
├── resources/           # Hugo 生成的资源缓存
├── static/              # 静态文件(图片、CSS 等)
├── themes/              # 主题目录
│   └── hugo-book/       # hugo-book 主题
├── hugo.toml            # 主配置文件
└── public/              # 生成的静态站点(不提交到 Git)

四、推送到 GitHub#

创建 GitHub 仓库#

  1. 登录 GitHub,创建新仓库
  2. 仓库名建议使用 username.github.io(自动获得 GitHub Pages 域名,但我们会用 Cloudflare)

初始化并推送#

# 初始化 Git(如果还没做)
git init

# 创建 .gitignore
cat > .gitignore << 'EOF'
public/
resources/_gen/
.DS_Store
.env
node_modules/
EOF

# 提交
git add .
git commit -m "init: Hugo blog with hugo-book theme"

# 关联远程仓库并推送
git remote add origin https://github.com/your-username/my-blog.git
git branch -M main
git push -u origin main

五、配置 Cloudflare Pages#

5.1 登录 Cloudflare#

  1. 访问 dash.cloudflare.com
  2. 注册或登录账号

5.2 创建 Pages 项目#

  1. 左侧菜单选择 Workers & Pages
  2. 点击 Create → Pages → Connect to Git
  3. 授权 GitHub 并选择你的博客仓库
  4. 配置构建设置:
设置项 值
Production branch main
Framework preset Hugo
Build command hugo
Build output directory public
  1. 添加环境变量:
变量名 值
HUGO_VERSION 0.164.0(与本地版本一致)
  1. 点击 Save and Deploy

5.3 自定义域名(可选)#

  1. 在 Pages 项目中点击 Custom domains
  2. 输入你的域名(如 blog.example.com)
  3. 按提示添加 CNAME DNS 记录:
    blog.example.com  →  your-project.pages.dev
  4. 等待 SSL 证书自动签发(通常几分钟)

5.4 自动部署#

配置完成后,每次推送到 main 分支,Cloudflare Pages 会自动:

  1. 拉取最新代码
  2. 执行 hugo 构建
  3. 部署到全球 CDN

六、配置 Decap CMS(可视化编辑器)#

Decap CMS(原 Netlify CMS)提供了一个网页端的文章编辑界面,让你不用接触代码就能写文章。

6.1 添加 CMS 文件#

创建 static/admin/index.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta name="robots" content="noindex" />
  <title>内容管理</title>
</head>
<body>
  <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
</body>
</html>

创建 static/admin/config.yml:

backend:
  name: github
  repo: your-username/my-blog    # 你的 GitHub 仓库
  branch: main
  base_url: https://your-worker.workers.dev  # OAuth Worker 地址
  app_id: your_github_app_id     # GitHub OAuth App ID

locale: zh_Hans

media_folder: "static/images"
public_folder: "/images"

collections:
  - name: "posts"
    label: "文章"
    folder: "content/posts"
    create: true
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
    path: "{{slug}}/index"
    fields:
      - { label: "标题", name: "title", widget: "string" }
      - { label: "发布日期", name: "date", widget: "datetime" }
      - { label: "草稿", name: "draft", widget: "boolean", default: false }
      - { label: "标签", name: "tags", widget: "list", allow_add: true }
      - { label: "分类", name: "categories", widget: "list", allow_add: true }
      - { label: "摘要", name: "summary", widget: "text", required: false }
      - { label: "正文", name: "body", widget: "markdown" }

6.2 创建 GitHub OAuth App#

  1. 访问 GitHub Developer Settings
  2. 点击 New OAuth App
  3. 填写信息:
设置项 值
Application name Blog CMS
Homepage URL https://your-domain.com
Authorization callback URL https://your-worker.workers.dev/callback
  1. 记录 Client ID
  2. 生成 Client Secret 并记录

6.3 部署 OAuth Worker#

使用 Cloudflare Worker 来处理 OAuth 认证流程。

安装 Wrangler CLI:

npm install -g wrangler

创建 Worker 项目:

mkdir cloudflare-worker && cd cloudflare-worker

创建 wrangler.toml:

name = "blog-oauth"
main = "src/index.js"
compatibility_date = "2024-01-01"

创建 src/index.js(OAuth 处理逻辑):

const ALLOWED_USERS = ["your-github-username"];

export default {
  async fetch(request, env) {
    const url = new URL(request.url);

    if (request.method === "OPTIONS") {
      return new Response(null, { headers: corsHeaders() });
    }

    switch (url.pathname) {
      case "/auth":
        return handleAuth(url, env);
      case "/callback":
        return handleCallback(url, env);
      default:
        return new Response("Not Found", { status: 404 });
    }
  },
};

function handleAuth(url, env) {
  const clientId = env.GITHUB_CLIENT_ID;
  const redirectUri = `${url.origin}/callback`;

  const githubAuthUrl = new URL("https://github.com/login/oauth/authorize");
  githubAuthUrl.searchParams.set("client_id", clientId);
  githubAuthUrl.searchParams.set("redirect_uri", redirectUri);
  githubAuthUrl.searchParams.set("scope", "repo,user");
  githubAuthUrl.searchParams.set("state", crypto.randomUUID());

  return Response.redirect(githubAuthUrl.toString(), 302);
}

async function handleCallback(url, env) {
  const code = url.searchParams.get("code");
  if (!code) {
    return new Response("Missing code", { status: 400 });
  }

  try {
    // 用 code 换取 access token
    const tokenRes = await fetch(
      "https://github.com/login/oauth/access_token",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
        },
        body: JSON.stringify({
          client_id: env.GITHUB_CLIENT_ID,
          client_secret: env.GITHUB_CLIENT_SECRET,
          code,
        }),
      }
    );

    const tokenData = await tokenRes.json();
    if (tokenData.error) {
      return new Response(`Error: ${tokenData.error_description}`, {
        status: 400,
      });
    }

    // 验证用户身份
    const userRes = await fetch("https://api.github.com/user", {
      headers: {
        Authorization: `Bearer ${tokenData.access_token}`,
        Accept: "application/vnd.github.v3+json",
      },
    });

    const userData = await userRes.json();
    if (!ALLOWED_USERS.includes(userData.login)) {
      return new Response("Access Denied", { status: 403 });
    }

    // 返回成功页面,传递 token 给 CMS
    return new Response(
      generateSuccessHtml(tokenData.access_token),
      {
        headers: { "Content-Type": "text/html;charset=UTF-8", ...corsHeaders() },
      }
    );
  } catch (error) {
    return new Response(`Error: ${error.message}`, { status: 500 });
  }
}

function generateSuccessHtml(token) {
  return `<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>授权成功</title></head>
<body>
  <p>授权成功,窗口将自动关闭...</p>
  <script>
    (function() {
      var token = "${token}";
      function receiveMessage(e) {
        var msg = "authorization:github:success:" + JSON.stringify({
          token: token, provider: "github"
        });
        window.opener.postMessage(msg, e.origin);
      }
      window.addEventListener("message", receiveMessage, false);
      if (window.opener) {
        window.opener.postMessage("authorizing:github", "*");
      }
      setTimeout(function() { window.close(); }, 3000);
    })();
  </script>
</body>
</html>`;
}

function corsHeaders() {
  return {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "GET, POST, OPTIONS",
    "Access-Control-Allow-Headers": "Content-Type",
  };
}

设置环境变量并部署:

# 登录 Cloudflare
wrangler login

# 设置 GitHub OAuth 密钥
wrangler secret put GITHUB_CLIENT_ID
# 输入你的 Client ID

wrangler secret put GITHUB_CLIENT_SECRET
# 输入你的 Client Secret

# 部署
wrangler deploy

部署成功后会得到一个 Worker 地址,如 https://blog-oauth.your-subdomain.workers.dev。

6.4 使用 CMS#

访问 https://your-domain.com/admin/ 即可打开 CMS 界面:

  1. 点击 Login with GitHub
  2. 授权后进入管理界面
  3. 可以可视化地创建、编辑、删除文章
  4. 编辑完成后点击 Publish,会自动提交到 GitHub 并触发部署

七、常用工作流程#

写文章(本地)#

# 创建新文章
hugo new content posts/my-new-post.md

# 本地预览
hugo server -D

# 编辑文章内容...
# 确认无误后提交
git add .
git commit -m "post: add my new post"
git push
# Cloudflare Pages 自动部署

写文章(CMS)#

  1. 访问 /admin/
  2. 点击 New 文章
  3. 填写标题、标签、正文
  4. 点击 Publish
  5. 自动提交到 GitHub 并部署

八、总结#

通过 Hugo + GitHub + Cloudflare Pages 的组合,我们搭建了一个:

  • ✅ 完全免费的博客系统
  • ✅ 全球 CDN 加速,访问速度快
  • ✅ 自动部署,推送即发布
  • ✅ 可视化编辑,降低写作门槛
  • ✅ Markdown 写作,内容可迁移
  • ✅ 版本控制,所有内容有迹可循

整套系统搭建完成后,你只需要专注于写作本身,技术细节交给自动化流程处理。

CC BY-NC-ND

本文采用 CC BY-NC-ND 4.0 协议进行许可。转载或引用须注明原作者及出处,不得用于商业用途,不得演绎。

署名 非商业性使用 禁止演绎