搭建自己的静态博客并自动部署
1. 概述
本文讲解本博客当前使用的搭建方式,并同步更新到 2026 年我这套实际可用的版本和部署流程。
| 部件 | 作用 |
|---|---|
| Hexo | 把.md文章转换为页面,并且快速部署到平台 |
| GitHub Pages | 提供从GitHub仓库创建网站的服务 |
| GitHub Actions | GitHub提供的免费持续集成服务 |
本文示例环境如下:
| 项目 | 当前版本 / 说明 |
|---|---|
| Node.js | 24.5.0 |
| Hexo | 8.1.2 |
| hexo-cli | 4.3.2 |
| 源码仓库 | my-hexo |
| 发布仓库 | Jayant-Tang.github.io |
Hexo + Github Pages
先从简单的讲起,看图:
简而言之:
- Hexo是一个博客框架,存放在你的电脑上,能把你的markdown文章转换成网站源码,并存入一个git仓库
- Github Pages是GitHub提供的免费静态网站服务,能让你展示文档。你只需要把网站源码存入一个repository,并且启用这个服务,GitHub就会帮你建一个静态网站,分配一个网址。别人从这个网址就能看到你的博客了。
- Hexo能一键把本地的网站源码推送到GitHub,让GitHub自动将其部署为Pages
GitHub Actions
前面的方法,网站所有的资源都存在本地,除了markdown文章和图片这种资源文件以外,网站的美化主题、配置等也存在本地。
这会产生几个问题:
-
数据容易丢失,一旦硬盘损坏,你的博客就灰飞烟灭了
-
只能在一台电脑上写文章,多台电脑之间hexo文件夹不好同步
-
网站的配置文件修改,没有历史记录追踪
-
由于网站的美化主题本身就是Github上的项目,每次要手动更新,很麻烦
-
每次写完文章,都要用hexo命令生成网站,还要用hexo命令部署网站,重复操作
所以,最好把hexo文件夹本身也当作Git项目,并放在GitHub托管。并且,还要把hexo部署的操作给自动化,让我们每次写完markdown文章,一 Push 就能自动部署页面。
这就要用到GitHub Actions,它是GitHub提供的持续集成(Continuous integration, CI)服务。这里不详述CI/CD的概念,你只需要知道,GitHub提供的这个服务,可以让你的仓库在特定条件下(例如push时),触发对应的动作(Actions)。
所谓的Actions,可以认为就是一条一条的Linux命令(也可以是Windows CMD命令)或者脚本,并且GitHub给你一个虚拟服务器去自动执行这一条一条的命令,这个虚拟服务器具有2核CPU,7GB内存,14GB硬盘。
你可以写自己的Actions,也可以用别人的Actions。Github提供了一个Action商店。
最终的效果应该如下图:
配置完成后的效果:
- 写完文章后,直接本地hexo仓库
commit,然后push - GitHub上的hexo仓库由
push触发Actions,自动在虚拟服务器中生成网站源码,然后部署到网站仓库中 - 网站仓库(xxxx.github.io)更新时,自动更新Github Pages
- hexo的美化主题,作为hexo的git submodule,每次push时自动checkout最新分支
2. 搭建步骤
一些简单的步骤就只简略提一下。
2.1. 在GitHub创建仓库并启用Pages
-
注册一个 GitHub 账号
-
先分清楚 GitHub Pages 的两种模式:
- 个人页(user page):GitHub 仓库名必须是
<用户名>.github.io,网址是https://<用户名>.github.io/ - 项目页(project page):GitHub 仓库名可以随意,例如仓库名叫
blog,网址就是https://<用户名>.github.io/blog/
这两种模式最关键的区别,不是“能不能部署”,而是最终网址根路径不同。
- 个人页(user page):GitHub 仓库名必须是
-
我的当前做法是创建两个仓库:
Jayant-Tang.github.io:真正用于发布博客,这是个人页my-hexo:Hexo 源码仓库,里面放文章、主题配置、工作流等
两个仓库都可以先留空,不创建任何文件。
-
然后设置 Pages,在发布仓库中:

在仓库的 Settings 中,选择 Pages,下方选择哪个分支的哪个目录被识别为网站的根目录。
GitHub 默认主分支通常是
main,但你也可以继续使用master。关键不是分支名,而是 Pages 配置、工作流配置和 Hexo 的 deploy 配置要保持一致。如果你之前已经使用过项目页,例如
https://<用户名>.github.io/<旧仓库名>/,后面又迁移到了个人页,那么旧仓库可以只保留一个跳转页,让旧链接自动跳到新站。
2.2. 本地部署hexo
安装软件
首先安装依赖,需要安装的软件:
- Git (git-scm.com):版本管理
- Node.js:hexo所需的平台
Windows用户安装时,一定要勾选
Add to PATH选项
然后安装hexo
1 | |
关于Node.js和hexo的版本依赖关系,请参考:文档 | Hexo
初始化hexo
找个喜欢的地方创建一个文件夹用于存放hexo项目,例如my-hexo。
进入这个仓库,然后进行hexo初始化
1 | |
注意:
- hexo初始化要求文件夹为空,因此必须先初始化hexo,再初始化git。否则
.git/的存在将导致hexo无法初始化hexo init时,产生了文件package.json,里面记录了hexo所依赖的npm包的具体来源和版本号npm install时,就根据package.json记录的内容进行了包的安装。包的具体安装情况记录在package-lock.json中
安装完毕后,目录结构应该如下
1 | |
初始化git并传到远程仓库
如果你从没用过git,需要配置本地的git用户和邮箱:
1
2>$ git config --global user.name <用户名>
>$ git config --global user.email <邮箱>这个用户名和邮箱只是本地提交代码时记录修改人是谁,并非是要登录什么网站
还是在这个目录,进行Git仓库初始化
1 | |
本文不解释git及其相关概念,后续使用git命令时,即使是复制,也务必明白你在做什么
另外,记得确认仓库中的
.gitignore文件,它是hexo init <文件夹>时生成的,其内容包括:
1
2
3
4
5
6
7
8.DS_Store
Thumbs.db
db.json
*.log
node_modules/ # 安装的npm包
public/ # 生成的静态网站源码目录
.deploy*/ # xxxx.github.io仓库
_multiconfig.yml 这是让git不要去追踪一些自动生成的内容,这些内容与文章、站点配置无关。
然后再commit:
1 | |
还记得前面在GitHub创建的my-hexo仓库还是空的吗,现在需要把本地my-hexo仓库绑定到GitHub仓库。
1 | |
本地预览hexo网站页面
依次输入以下命令
1 | |
终端中会打印本地网站的地址,Ctrl+鼠标左键点击访问即可:

可以看到默认页面即为成功,首页的唯一一篇文章是hexo自带的source/_post/hello-world.md.
终端按Ctrl + C停止本地服务器
2.3. 配置hexo
安装主题
hexo有很多美化主题,可以在Themes | Hexo找到喜欢的主题,也可以在GitHub上搜索其他主题。我当前博客实际使用的是 Fluid 主题,不过这里仍以 Butterfly 的安装流程举例,因为主题安装思路本质上是相通的:安装主题、拷贝主题配置、在 Hexo 主配置中启用主题。
主题放在themes文件夹下。不论安装什么主题,一定要注意:
- 虽然主题的官方教程一般都是让用
git clone到themes文件夹下,但是我们的hexo文件夹已经是一个git仓库了,所以要以git submodule的形式安装; themes下的主题文件夹必须以<主题名>来命名,而不是GitHub仓库的名称,如hexo-theme-<主题名>。- 很多主题的官方说明会要求安装其他的npm包。使用
npm安装这些包时,一定要带--save选项。这样这些包才会被记录到package.json中,这样我们以后push时,GitHub Actions上面的虚拟服务器才会帮我们完整的安装这些依赖。 - 主题本身的配置文件在
themes/<主题名>/_config.yml中,但这个文件是被主题仓库管理的,不会被hexo仓库管理,无法推送到GitHub。而且主题仓库更新后我们修改的配置也会丢失。所以我们应当在hexo根目录下创建_config.<主题名>.yml文件,并把主题中的配置文件拷贝进去。这样配置文件就会被hexo仓库管理。(但是原来主题文件夹里的_config.yml也不要删除)
下面是我安装butterfly的步骤:
- 把butterfly作为git submodule安装到hexo中
1 | |
注意:
git submodule add时,一定要指定主题的文件夹名称,将其命名为主题的名称,此处为themes/butterfly。若不这样做,将会变成其GitHub仓库的名称,如hexo-theme-butterfly,这样是不行的。
- 安装渲染器
根据butterfly的官方文档,需要安装渲染器。其他主题可能需要其他渲染器。
1 | |
一定要带
--save
- 拷贝主题配置文件
把themes/<主题名>/_config.yml配置拷到hexo根目录下
1 | |
- 应用主题
修改hexo配置文件,my-hexo根目录下的_config.yml,找到theme项并修改:
1 | |
yml格式的
:后面一定要有一个空格
- 查看效果
1 | |

- 其他修改
根据主题的官方说明,进行其他的自定义配置。所有的修改都在my-hexo根目录下_config.<主题名>.yml中修改。
themes/<主题名>/_config.yml虽然不生效,但是也不能删除。
配置hexo
hexo本身的配置,需要修改my-hexo根目录下的_config.yml文件。可以修改站点名称、作者等,具体可以参考官方文档:配置 | Hexo。
这里提一些与Github Pages还有markdown文章插入相关的配置。
网站url与root
这里一定要把个人页和项目页分开写清楚:
-
个人页(user page)
仓库名必须是
<用户名>.github.io,例如我的当前站点:https://jayant-tang.github.io/对应的 Hexo 配置应该是:
1 | |
-
项目页(project page)
GitHub 仓库名可以随意,但最终网址里的路径名默认就是仓库名。
例如,如果你的 GitHub 仓库名就叫:
blog那么项目页地址通常就是:
https://jayant-tang.github.io/blog/对应的 Hexo 配置应该是:
1 | |
-
不要把两种模式配混了
- 个人页必须是
root: / - 项目页必须是
root: /<仓库名>/ url也要和最终访问地址完全一致- 对于项目页,
<仓库名>不是你自己随便起的一个路径别名,而是你在 GitHub 上创建的那个 Pages 仓库名
- 个人页必须是
我这个博客一开始用的是项目页,后来迁移到了个人页,所以现在实际使用的是第一种配置。
不同环境,访问资源的根目录是不同的。hexo的所有资源文件都应该放在
<hexo文件夹>/source下,生成网页时,会自动拷贝到网页的根目录下。 例如,有一张图片资源的路径是:
<hexo文件夹>/source/foo/bar/image.png:
- 对于你的markdown文章,可以设置根目录为
<hexo文件夹>/source- 对于网站,则取决于你到底是个人页还是项目页
只要设置好根目录,markdown和网页就可以使用相同的路径来访问到相同的资源了,如
/foo/bar/image.png。
hexo的部署方式介绍
通常情况下,hexo的部署流程是这样的:

hexo g,生成网页源码,并存储在/public文件夹中hexo d,把public同步到本地git仓库,然后推送到github仓库。
先安装 git 部署的插件:
1 | |
然后需要给 Hexo 配置 GitHub 发布仓库的路径和权限,打开hexo配置文件:
1 | |
| 选项 | 说明 |
|---|---|
| type | 部署类型,可以设置多个要部署的目标 |
| branch | 分支名称 |
| repo | 发布仓库地址,这里有 ssh 和 https 两种方式,推荐 ssh |
GitHub认证方式:
- https方式,任何人可以随便clone,但是push时通常要走 token 或网页认证;
- ssh方式,需要ssh key,公钥放在GitHub,私钥自己持有。只要持有私钥就可以用ssh访问GitHub仓库,纯命令行操作,不会弹窗。
我现在本地和 GitHub Actions 都使用 ssh 方式发布。
ssh密钥对的生成
然后需要生成两对ssh key,第一对用于电脑和GitHub之间的认证,第二对用于GitHub my-hexo仓库 和GitHub xxxx.github.io仓库之间的认证。
生成key:
1 | |
-t:选择加密算法,ed25519比较安全
-C: 密钥的备注说明文字。Github要求这里要填上git config时配置的邮箱,同时也必须是GitHub中已经配置的认证邮箱。
-f:生成的私钥文件名
执行完毕后,~/.ssh下应该有4个新文件,其中带有.pub后缀的是公钥,不带的是私钥:
1 | |
GitHub ssh key设置
配置你的电脑ssh访问GitHub的权限。GitHub点击右上角头像-Settings,然后点击New SSH key:

然后,把github_ed25519.pub的内容粘贴进去,其他都不用填,确认即可:
1 | |
测试:
1 | |
有以上输出说明权限配置成功。
配置主题
根据你所使用的主题的官方文档来配置。建议把后面的其他部署都完成后,再研究主题的配置。
2.4. 从电脑部署hexo
前面的步骤都完成后,可以执行:
1 | |
这时,hexo应该已经可以把网站配置到xxxx.github.io上了,过一会从网页端就可以看到网站页面了。
网页端的入口:
Pages仓库右边:

然后点击View deployment

2.5. GitHub Actions自动部署
pages仓库添加公钥
我们需要设置my-hexo仓库访问xxxx.github.io仓库的权限,前者持有私钥,后者持有公钥。然后才能在push完my-hexo仓库之后,让my-hexo仓库的Actions自动部署博客到Pages仓库。
首先进入xxxx.github.io仓库页面,找到Settings - Deploy Keys,然后点击Add deploy key:

把hexo_ed25519.pub的内容粘贴进去:
1 | |
Title填写HEXO_DEPLOY_PUB
hexo仓库添加私钥
然后进入my-hexo仓库,找到Settings - Secrets - Actions,然后点击New repository secret.

把私钥hexo_ed25519中的内容粘贴进去:
1 | |

注意,Name要记住,后面需要用到。我这里配置的是HEXO_DEPLOY_PRI.
编写Actions
在本地my-hexo文件夹中,创建一个文件.github/workflows/main.yml,填入以下内容:
1 | |
内容修改:
修改环境变量,改成你自己的用户名(
GIT_USER)、邮箱(GIT_EMAIL)、Pages 仓库、Pages 分支修改
node_version: [xxxx],填上你要使用的 Node.js 版本。我当前环境是 Node.js 24.5.0,所以这里示例写的是24
1
2# 查看本地Node.js版本
node -v
- 修改时区,如果在中国就不用改了
内容解释:
- 在 hexo 仓库的
master分支收到 push 时,触发 job- 在 Ubuntu 环境中 checkout 本仓库的最新内容,并自动获取 submodule
- checkout Pages 发布仓库,并把它放到
.deploy_git路径中- 安装指定版本的 Node.js
- 配置环境变量:添加私钥、配置 git 用户和邮箱。注意,这里的私钥名称就是
${{secrets.HEXO_DEPLOY_PRI}},与前面配置的HEXO_DEPLOY_PRI一致- 根据
package.json的内容安装 npm 包- 执行
npm run deploy,本质上就是hexo deploy其他说明:
- 我没有使用别人封装好的自动部署 Hexo 的 Action,而是直接把流程写开。这样 Node.js 版本、ssh、主题、额外脚本都更可控。
提交修改
从一开始到现在,已经修改了不少内容了,将其commit然后push
1 | |
add之前一定要注意检查.gitignore,是否已经让git忽略了
db.json,node_modules/,public/。
push之后,在Github Actions页面应该已经可以看到流程了:

你的每次push,都会触发actions。绿色勾说明整个流程没有出现错误(指Ubuntu linux命令没有出现执行错误的)。如果执行失败,会出现红色叉,你可以点进去看详细的执行记录。
Actions执行完毕后,应该已经部署到Pages仓库了,这里也可以看到网站部署的记录:


绿色说明部署成功,网站已经可以访问了。
3. 编写并发布文章
3.1. 文章的存储与格式
所有要显示的markdown文章都存放在source/_posts/目录下,不展示的文章(草稿)都放在source/_drafts目录下。
新建文章:
1 | |
layout: markdown的布局(或者说模板),布局存放在
scaffolds/目录下,默认有三种布局:title:文章的标题,直接写标题即可,不需要写.md。但是最好不要有空格等特殊字符!!!
以上命令,会在source/_posts/目录下自动创建一个新的<title>.md文件,并且把模板内容拷贝过去。由于模板中有date参数,这时会自动记录当前的电脑本地时间作为这篇文章的创建时间,记录在Front-Matter中。
3.2. Front-Matter
Front-Matter是markdown文件最上方以两行---分隔的区域,里面需要填写yml格式的文章配置,例如:
1 | |
在Typora编辑器中,直接在文章开头输入---并回车,就可以创建这个区域:

对于某些主题来说,有些选项是必选的,具体要看对应主题的说明文档。比如,我使用的butterfly主题,就可以配置:
-
title:文章标题
-
date:文章创建日期
-
cover:文章封面图
-
tags:文章标签
-
categories:文章目录
-
…
tags和categories的区别:
tags是无顺序的,categories是一个递归层级。设计好的tags和categories能让读者很方便的从主页索引到对应的文章。
这里,我们也可以体会到前面模板的意义:可以在模板里填好Front Matter,这样每次hexo就会自动帮我们生成了。
3.3. Typora在文章中插图的问题
方案一:云端图床
图片全部放在云端图床,markdown和Pages都通过URL查看图片。具体的方式,本文不介绍,可以去网上查阅。
方案二:图片放在本地
假设我们在markdown中展示一张图片,其路径为source/imgs/foo/bar/image.png。
hexo生成网站后,网页会根据 ${root}/imgs/foo/bar/image.png来找这张图片,即是说,source文件夹放的内容,在网页端就是根目录下的内容。
在2.3 配置hexo中,我们已经配置了root为
<仓库名>了。
下面要解决的是,如何在markdown文章和网页中,能用同样的路径来访问图片资源。这样本地写文章时和网页端就都可以预览了,而且从markdown转换成html时,路径也是可用的。
解决方式就是根目录,由于hexo的根目录前面已经设置过了,我们只需要设置markdown编辑器的根目录就好。设置完毕后,而这就都可以通过/imgs/foo/bar/image.png来访问同一张图片了。
(1)设置markdown文章访问图片时的根目录
我使用的Markdown编辑器是Typora,Typora支持针对每一篇文章单独设置根目录。
直接在Front-Matter中填入以下内容即可:
1 | |
由于文章的路径是
source/_post,所以./..就是source了。
你可以把这一行配置直接写进scaffolds/post.md中,这样下次模板就自动帮你填了。
以上设置,直接从菜单栏设置是一样的,Typora会自动帮你写这一行:

(2)设置Typora粘贴图片时的行为
直接在Front-Matter中写入:
1 | |
这个效果和下图中的偏好设置是一样的,但是不要去偏好设置里配置,因为那是全局的配置:

只需要在 Front-Matter 中写就行,不要去偏好设置里设置成上图的样子!!!
(3)检查效果
在Front-Matter中填入以上配置之后,每次粘贴图片到Typora时,就会自动帮你把图片拷贝到source/imgs/<文章名>.assets/文件夹中。
我们可以粘贴一张图片到typora中,检查图片位置:

然后,检查markdown访问图片的路径,查看markdown源码:

可以看到,路径名直接是从/imgs开始的,这说明根目录已经是source了。
以上配置全部写在Front-Matter中,把这两行配置写入
scaffolds/post.md的Front-Matter中,这样模板就创建好了。这些配置就只对hexo中的markdown有影响,对你平时用typora写其他文章没有任何影响。
3.4. Typora中的html标签插图
如果你在Typora中右键点击一张图片,然后进行缩放,它就会从Markdown语法的图片:
1 | |
变成html格式的img标签,比如缩小到50%:
1 | |
hexo在把.md文章转换成html的时候,会自动根据我们设置的根目录来替换 markdown 格式图片的路径。
这里也要区分个人页和项目页:
- 如果你是个人页,站点根路径就是
/,那么最终还是/imgs/image.png - 如果你是项目页,例如
/blog/,那么会被转换成/blog/imgs/image.png
但是,如果.md文章里的图片本来就是 html 格式的标签,hexo不会对其进行任何转换。因此:
- 对于个人页:
<img src="/imgs/image.png">本身就是对的,通常不需要额外处理 - 对于项目页:
<img src="/imgs/image.png">不会自动变成/<仓库名>/imgs/image.png,这时才需要额外处理
我现在的博客已经迁移到个人页,所以这一步已经不需要了。
如果你仍然使用项目页,例如仓库名叫blog,那可以在 CI 里做一次替换:
1 | |
以上三个命令会把<img src="/imgs/、<img src="./imgs/和<img src="imgs/都替换成<img src="/blog/imgs/。
默认情况下,html标签中的写法:
./imgs和imgs是一样的,在网页端会被当作/<文章名>/imgs。
/imgs不会被转换,在网页端还是/imgs对于项目页,网页上最终能访问到的其实是
/<仓库名>/imgs/...;对于个人页,则直接是/imgs/...。
把这些命令加到GitHub Actions中,这样我们本地用typora的时候还是能看到图片,push到远程的时候网页也能看到图片。
首先在GitHub Actions的.yml文件中增加一个环境变量,方便后面更改:
然后,在hexo部署的步骤之前,加入sed命令:
1 | |
这样云端部署的时候就会自动帮你替换路径了。
但是,本地
hexo s --debug去查看网页的时候,由于没有Actions帮你转换,图片还是看不了的。这个问题倒没什么影响。
3.5. Typora云端和本地图片互转
如果你有一篇文章,以前所有的图片是放在云端的,现在想放在本地。可以批量操作:

只要文章已经写好了Front-Matter,那么整个复制过程会从你设置的图片根目录,来自动设置图片的路径。
同理,也可以批量把本地图片上传到云端(需要提前设置好上传方式),文章内所有的图片路径都会自动更新。
3.6. 文章的发布
文章写好以后,可以hexo s --debug在本地查看一下。也可以hexo d直接从本地发布。但是既然我们已经搭建好CI系统,可以直接add,commit然后push,后面的事情就交给GitHub Actions完成了。
Github Actions会在虚拟服务器上,根据我们写好的yml,自动执行hexo g然后hexo d,这样网站就重新部署好了。
但是这又会产生新问题:我们每次发布新文章,所有旧的文章都会被重新部署一遍,导致整个网站所有文章的更新日期都变成一样的了:

4. 网站的其他设置
基本概念
博客除了文章外,还有页面。页面的模板就是scaffolds/post.md。可以有时间线、标签、目录等页面,页面也存放在source目录下。
在_config.yml中,配置了这些页面的目录:

基本上所有的配置你都可以在配置 | Hexo 和你所用的主题的官方文档中找到。
文章的永久链接(Permalinks)
hexo默认的文章URL格式是/年/月/日/文章标题,如果你的文章标题有中文,这个URL就会巨长无比:

我的方案是改成用文章标题+日期的hash值去命名url,这样更简洁。
打开站点配置_config.yml,修改:
1 | |
这样,文章的URL就会变成:

这样URL就比较短了。
文章的更新日期
文章具有创建日期(date)和更新日期(updated)。默认情况下,更新日期使用的是操作系统中记录的文件最后修改日期。
但是我们利用Github Actions部署时,由于每次部署都会重新拉取仓库,文件的修改日期会变成拉取的时间,导致所有文章的更新日期都会变成部署时间。
其他注意事项
- 所有文章标题、文件名、目录名都不要有空格等特殊字符!!!(可以有减号
-) - 本博客使用了Gitalk作为评论系统,把页面仓库的Github Issue变成一个评论区。会有一些API key之类的东西,明文配置在hexo仓库中,所以我把hexo仓库的权限设为私有了
- Front-Matter是yml格式,冒号
:后面一定要加一个空格,不然识别不了。