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

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.mdtags/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.txtsitemap.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 真的包含 tagscategories,并且已创建带正确 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 历史才是自己真正拥有的东西。

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

参考资料