别再把个人博客搭建想复杂了:从买域名到手机发文的完整指南

Chen Xi
Chen Xi

搭个人博客看起来像一件很简单的事:选个主题,写篇文章,再把网页放到网上。

真正做起来才会发现,问题不是“怎样生成一个首页”,而是怎样让写作、备份、构建、部署、域名和搜索引擎长期配合。我的博客也踩过不少坑:CMS 保存后没有触发 Actions、公式生成失败、主题样式在手机和电脑上同时错位、DNS 改完却忘了核对邮件记录。

回头看,这些问题大多来自同一个原因:一开始只看到了网页,没有先想清楚整个发布系统。

这篇文章从零整理一条完整路线。最终效果是:

  • 文章使用 Markdown 保存;
  • 源码和历史版本在 GitHub;
  • GitHub Actions 自动构建并阻止错误上线;
  • Cloudflare Pages 提供 HTTPS 和全球访问;
  • 自定义域名作为唯一正式地址;
  • Pages CMS 让电脑和手机都能在线写作。

image

本文使用 Node.js 22、Hexo 8.1.2 和 Keep 4.3.0 说明。软件版本会继续变化,安装时应同时查看文末官方文档。

先决定:为什么选择静态博客

个人博客常见方案大致分为两类。

方案 优点 代价
WordPress 等动态 CMS 后台完整、插件多、多人协作方便 需要数据库、服务器维护和安全更新
Hexo 等静态生成器 快、便宜、文件可备份、攻击面小 评论、搜索和在线编辑需要额外方案

如果目标是个人写作、技术文章和长期归档,我更喜欢静态博客。文章就是 Markdown 文件,即使未来不用 Hexo,也能迁移到其他系统。

Hexo 的职责非常单纯:

1
2
3
4
5
Markdown + 主题 + 配置
↓
Hexo 构建
↓
HTML + CSS + JavaScript

Cloudflare Pages 负责托管生成结果,GitHub 保存源码和版本历史。各部分可以替换,不会把内容锁死在某个平台。

第一步:准备环境

需要准备:

  • Node.js;
  • Git;
  • GitHub 账号;
  • 一个代码编辑器;
  • 可选的个人域名。

安装后先检查:

1
2
3
node --version
npm --version
git --version

然后安装 Hexo 命令行:

1
2
npm install --global hexo-cli
hexo version

如果 PowerShell 提示无法运行 npm.ps1,可以暂时使用 npm.cmd;不要为了一个命令就随意关闭整台电脑的脚本安全策略。

第二步:创建 Hexo 项目

初始化一个新项目:

1
2
3
hexo init my-blog
cd my-blog
npm install

Hexo 会生成几个关键位置:

1
2
3
4
5
6
7
my-blog/
├─ _config.yml # 站点配置
├─ package.json # 依赖和命令
├─ scaffolds/ # 新文章模板
├─ source/
│ └─ _posts/ # Markdown 文章
└─ themes/ # 主题目录

先不要急着改主题。执行:

1
hexo server

浏览器打开 http://localhost:4000/。默认博客能够显示,说明 Node、依赖和 Hexo 本身没有问题。先建立这个基线,后面出现故障才知道是主题还是环境。

第三步:安装 Keep 主题

本站使用 Keep。把主题作为 npm 依赖安装,比手动复制一份主题目录更容易升级:

1
npm install hexo-theme-keep

在 _config.yml 中设置:

1
theme: keep

再把主题默认配置复制到项目根目录:

1
Copy-Item node_modules/hexo-theme-keep/_config.yml _config.keep.yml

网站基础信息可以这样写:

1
2
3
4
5
6
7
8
9
# _config.yml
title: 我的博客
subtitle: 记录技术与生活
description: 分享编程、人工智能与个人思考
author: Your Name
language: zh-CN
timezone: Asia/Shanghai
url: https://example.com
permalink: :year/:month/:day/:title/

主题信息放在 _config.keep.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
base_info:
title: 我的博客
author: Your Name
avatar: /images/avatar.jpg
logo: /images/logo.png
favicon: /images/favicon.png

menu:
home: /
archives: /archives
tags: /tags
categories: /categories
about: /about

主题升级后,尽量把自己的修改保留在根目录配置和 source/ 中,不要直接改 node_modules。否则重新执行 npm install 后,修改可能全部消失。

第四步:写第一篇文章

创建文章:

1
hexo new post "hello-blog"

文件会出现在 source/_posts/。一篇比较完整的文章头部可以写成:

1
2
3
4
5
6
7
8
9
10
11
---
title: 我的第一篇博客
date: 2026-07-30 23:00:00
updated: 2026-07-30 23:00:00
published: true
description: 记录这个个人博客从本地 Markdown 到正式域名的第一次完整发布。
categories: 随笔
tags:
- Hexo
- 博客
---

description 不只是给搜索引擎看的,它还迫使作者用一句话说明文章到底解决什么问题。不要让所有文章都复用网站简介。

分类通常只保留一个主要方向,标签可以有多个。这样既不会生成过多空洞分类,也方便以后整理专题。

如果启用了菜单中的分类、标签和关于页面,还要创建对应页面:

1
2
3
hexo new page categories
hexo new page tags
hexo new page about

然后在 categories/index.md 和 tags/index.md 的 front matter 中分别增加:

1
type: categories

以及:

1
type: tags

只配置菜单却没有页面,是 Tags 和 Categories 打开后一片空白的常见原因。

第五步:把构建固定成命令

在 package.json 中增加:

1
2
3
4
5
6
7
8
9
{
"scripts": {
"clean": "hexo clean",
"build": "hexo generate",
"preview": "hexo server",
"deploy": "hexo deploy",
"release": "npm run clean && npm run build && npm run deploy"
}
}

日常本地检查:

1
2
3
npm run clean
npm run build
npm run preview

对应的 Hexo 三件套是:

1
2
3
hexo clean
hexo generate
hexo deploy

clean 不能总省略。主题、路由或生成器发生变化后,旧的 public/ 和数据库可能留下已经不存在的页面。

第六步:理解 source 和 public

Hexo 项目里最容易混淆的两类文件是:

  • source/:你真正维护的文章和静态资源;
  • public/:Hexo 每次构建生成的网站。

不要直接修改 public/。下一次构建会覆盖它,而且源码仓库中找不到修改依据。

本站进一步使用两个 Git 分支:

分支 保存内容
source Markdown、配置、主题依赖、脚本
main public/ 中生成后的静态网站

第一次搭建也可以只用一个源码分支,让 Cloudflare Pages 执行 npm run build。等博客稳定后,再升级到双分支和发布检查。两种架构都能工作,关键是不要把它们混在一起。

更详细的两种部署方式,可以参考本站的 Hexo 8 + Keep 4 部署记录。

第七步:把源码交给 GitHub

在 GitHub 创建空仓库后,本地执行:

1
2
3
4
5
6
git init
git add .
git commit -m "chore: initialize Hexo blog"
git branch -M source
git remote add origin https://github.com/USERNAME/REPOSITORY.git
git push -u origin source

提交前检查 .gitignore,至少不要提交:

1
2
3
4
node_modules/
public/
.deploy_git/
db.json

必须提交 package-lock.json。GitHub Actions 和 Cloudflare 才能通过 npm ci 安装与本地一致的依赖版本。

第八步:先完成一次手动部署

安装 Hexo Git 部署器:

1
npm install hexo-deployer-git

在 _config.yml 中设置:

1
2
3
4
5
deploy:
type: git
repository: https://github.com/USERNAME/REPOSITORY.git
branch: main
ignore_hidden: false

然后执行:

1
npm run release

部署器会把 public/ 推到 main。此时 GitHub 中应同时存在源码分支和生成分支。

使用私有仓库或自动化令牌时,不要把令牌直接写进 _config.yml。凭据应放在系统凭据管理器或 GitHub Secrets 中。

第九步:让 GitHub Actions 自动发布

在 .github/workflows/deploy-hexo.yml 创建工作流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
name: Build and deploy Hexo

on:
push:
branches:
- source
workflow_dispatch:

permissions:
contents: write

concurrency:
group: hexo-production
cancel-in-progress: true

jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Check out source
uses: actions/checkout@v5

- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Build
run: |
npm run clean
npm run build

- name: Publish generated site
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git clone --depth 1 --branch main \
"https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
"$RUNNER_TEMP/deploy"
rsync -a --delete --exclude ".git/" public/ "$RUNNER_TEMP/deploy/"
cd "$RUNNER_TEMP/deploy"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add --all
if git diff --cached --quiet; then
exit 0
fi
git commit -m "Site updated"
git push origin main

这个工作流的意义不只是省去一次命令。以后还可以在 Build 阶段加入断链、公式、SEO 和图片检查。检查失败时不更新 main,正式网站继续保留上一个正常版本。

GitHub 官方也建议 Node 项目在 Actions 中固定 Node 版本并使用 npm ci,避免构建环境随运行器变化。

第十步:接入 Cloudflare Pages

进入 Cloudflare:

1
2
3
4
Workers & Pages
→ Create application
→ Pages
→ Connect to Git

选择 GitHub 仓库。

如果 Cloudflare 连接的是 Hexo 源码分支:

1
2
3
Production branch: source
Build command: npm ci && npm run build
Build output directory: public

如果连接的是已经生成静态网站的 main:

1
2
3
4
Production branch: main
Framework preset: None
Build command: 留空
Build output directory: 仓库根目录

本站使用第二种方式:GitHub Actions 先构建检查,Cloudflare 只发布通过检查的静态结果。

部署完成后会得到:

1
https://PROJECT.pages.dev/

先确认这个地址正常,再处理自定义域名。不要在构建还失败时同时折腾 DNS,否则很难区分问题发生在哪一层。

第十一步:绑定域名和 DNS

在 Pages 项目中进入:

1
2
Custom domains
→ Set up a domain

输入 example.com。根域名需要位于同一个 Cloudflare 账户并使用 Cloudflare 名称服务器;Cloudflare 会协助创建记录和签发证书。

子域名通常使用:

1
2
3
4
类型: CNAME
名称: www
目标: PROJECT.pages.dev
代理状态: 已代理

不要只在 DNS 页面创建记录,却不在 Pages 项目中添加 Custom domain。Pages 还需要知道应为哪个域名提供站点和证书。

如果域名同时用于邮箱,网站记录和邮件记录要分开处理:

  • 网站通常使用 CNAME;
  • 邮件接收依赖 MX;
  • SPF、DKIM、DMARC 通常使用 TXT;
  • 邮件相关记录不要开启 Cloudflare 代理。

第十二步:上线前补齐 SEO

至少检查:

  • _config.yml 中的 url 是正式域名;
  • 每页有唯一 <title> 和 description;
  • canonical 指向正式域名;
  • 文章只有一个主标题;
  • 图片有准确的 alt;
  • 分类、标签和归档页不为空;
  • 存在 robots.txt 和 sitemap.xml;
  • 404 页面能正常返回;
  • pages.dev 不与正式域名重复收录。

安装 sitemap 和 RSS:

1
npm install hexo-generator-sitemap hexo-generator-feed

source/robots.txt 可以先写:

1
2
3
4
User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml

上线后把 sitemap 提交到 Google Search Console 和 Bing Webmaster Tools。SEO 的基础不是关键词堆砌,而是让每个地址稳定、内容明确、内部链接可被发现。

第十三步:接入 Pages CMS

博客已经稳定发布后,再解决“怎样方便写文章”。

Pages CMS 会读取仓库根目录的 .pages.yml,把 Markdown 字段映射成在线表单。一个精简配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
media:
input: source/images/posts
output: /images/posts
categories: [image]
rename: safe

content:
- name: posts
label: 博客文章
type: collection
path: source/_posts
format: yaml-frontmatter
fields:
- name: title
label: 标题
type: string
required: true
- name: date
label: 发布时间
type: date
required: true
- name: description
label: SEO 摘要
type: text
required: true
- name: categories
label: 分类
type: string
- name: tags
label: 标签
type: string
list: true
- name: body
label: 正文
type: rich-text
required: true

media.input 是图片在仓库中的保存位置,media.output 是写进文章的公开 URL。两者混淆后,CMS 看似上传成功,线上却会出现断图。

Pages CMS 还可以添加按钮触发 workflow_dispatch。本站的实际配置、图片上传和 Actions 排查过程写在 Pages CMS + Hexo 8 实战 中。

第十四步:固定日常发布流程

以后每次写文章只需要:

1
2
3
4
5
6
Pages CMS 或本地编辑 Markdown
→ 保存到 source
→ GitHub Actions 构建与检查
→ main 更新
→ Cloudflare Pages 发布
→ 正式域名出现新文章

本地发布时:

1
2
3
4
5
6
7
git pull
npm ci
npm run clean
npm run build
git add .
git commit -m "content: publish new post"
git push

CMS 发布时则只需填写标题、摘要、分类、标签和正文。它不是另一套数据库,最终保存的仍然是同一批 Markdown 文件。

最容易遇到的几个问题

本地正常,线上样式错乱

先比较本地 public/、GitHub main 和正式域名,不要马上加 CSS 补丁。确认主题源码有没有被当作文本输出、生成分支是否完整、浏览器是否仍在使用旧缓存。

本站曾经出现 Logo 被拉长、Stylus 源码进入 HTML 的情况,完整排查见一次 Hexo 主题样式事故复盘。

CMS 保存了,Actions 没有执行

依次检查:

  1. CMS 保存到哪个分支;
  2. workflow 的 on.push.branches 是否包含该分支;
  3. workflow 文件是否已经存在于对应分支;
  4. GitHub Actions 是否启用;
  5. Pages CMS GitHub App 是否获得仓库权限。

Tags 和 Categories 没内容

确认文章 front matter 真的包含 tags 和 categories,并且已创建带正确 type 的聚合页面。标签大小写也应统一。

公式显示为原始文本

Markdown 渲染器、KaTeX 插件和主题样式必须配套。升级 Hexo 后应重新构建所有含公式文章,并检查生成 HTML 中有没有未渲染的公式分隔符或 KaTeX 错误。

GitHub 已更新,域名仍是旧内容

先看 Actions 是否成功,再看 main 是否出现新 HTML,然后查看 Cloudflare 最新部署是否对应同一提交。只有这些都正确,才进入浏览器和 CDN 缓存排查。

上线检查清单

image

第一次宣布博客上线前,至少完成:

  • 首页、文章页、分类和标签页都能打开;
  • 手机和电脑没有明显布局错位;
  • 新文章可以从写作端一路自动发布;
  • GitHub 保存完整源码,而不只有 public/;
  • 域名、HTTPS、canonical 和 sitemap 使用同一个正式地址;
  • 404、robots、RSS 和隐私页面存在;
  • GitHub 开启双因素认证;
  • 没有把令牌、邮箱密码或 API 密钥提交进仓库;
  • 知道怎样回退到上一个正常提交。

最后:真正需要维护的是写作系统

一个个人博客不需要在第一天拥有评论、广告、统计、邮件订阅和几十个插件。先让下面四件事可靠:

  1. 文章不会丢;
  2. 发布可以重复;
  3. 错误能够阻止上线;
  4. 更换平台时内容仍能迁移。

域名是门牌,主题是装修,Cloudflare 是房屋托管;Markdown 和 Git 历史才是自己真正拥有的东西。

博客搭好以后,最理想的状态不是每天研究部署,而是打开页面就能写。剩下的复杂流程安静地待在后面,只有出错时才提醒你。

参考资料