☰
Hexo博客部署全攻略:GitHub Pages与云服务器自动发布实战
2026/10/24 1:13:40 网站建设 项目流程

这套Hexo博客部署方案,我前后完整折腾了三轮。第一轮跟着教程半小时跑通了GitHub Pages,以为万事大吉;第二轮加了服务器自动部署,结果在Nginx路径和Git钩子权限上连续踩坑;第三轮才把所有流程理顺,沉淀出一套自己维护起来很舒服的工作流。标题里写“坑已踩完”,不是标题党,是这几轮下来确实把该踩的地方都踩过了。这篇文章就把完整的部署链路、遇到过的问题、以及最终的解决方案一次性整理清楚,给准备用Hexo建站的朋友做参考。

这篇内容适用的场景很明确:你想用Hexo生成静态博客,用GitHub Pages做免费托管,同时希望博客部署到自己的云服务器上实现全流程掌控,并且不想每次发文章都手动传文件。无论你是第一次接触Hexo,还是已经用了一段时间想加服务器部署,这篇都能帮你少走弯路。

1. 为什么我最终选择了Hexo加服务器这套组合

先说结论:这套组合的核心价值在于,它把“写作、构建、发布”三个环节彻底拆开了,每个环节都用了最适合的工具。

1.1 静态博客和动态博客的本质区别

很多人一开始纠结用WordPress还是Hexo。我的看法很简单:如果你主要写文字、写技术笔记、记录思考,不需要用户注册、评论审核、会员系统这些复杂功能,那静态博客就是更优解。Hexo生成的是一堆纯静态的HTML、CSS、JavaScript文件,不需要数据库,不需要服务端动态解析,这意味着理论上你可以把它丢到任何Web服务器上运行。而WordPress这类动态博客虽然后台编辑体验成熟,但维护成本摆在那里——要升级核心、清理插件漏洞、定期备份数据库,任何一环出问题都可能打不开站点。

静态博客让我比较安心的一点是:站点内容本质上是文件夹里的Markdown文件。换主题、换部署平台、甚至Hexo哪天不维护了,你的内容资产依然在本地实实在在躺着,不存在被某个平台或某个系统绑死的问题。

1.2 GitHub Pages和云服务器各自的定位

GitHub Pages是免费的静态托管方案,配合Git工作流非常舒服:本地写文章,hexo g生成静态文件,git push推上去,线上就更新了。但纯GitHub Pages的局限也很明显:只能托管静态内容,想加一些服务端能力、自定义HTTP头、精细化的缓存策略,都会束手束脚。

云服务器补上的正是这些缺位:能自己配置Nginx、能挂独立域名并自由管理DNS解析、能加SSL证书、能自定义缓存规则。但服务器也有服务器的麻烦,最典型的是“发布链路”要自己搭。手动上传文件的方式,第一天觉得新鲜,重复十次之后就会怀疑人生。

我的选择是两者结合:GitHub Pages作为备份和临时预览站点,云服务器作为正式站点。写文章时一次构建,推送到GitHub触发Pages更新,再通过Git钩子触发服务器自动拉取发布。听起来复杂,跑通后实际体验就是一键发布。

1.3 与其他方案的横向对比

方案成本发布流程可控性适合人群
纯GitHub Pages免费本地构建后推送中,受限于平台刚开始建站的用户
纯云服务器服务器费用需自行搭建立或手动上传高有Linux基础的人
GitHub Pages + 云服务器仅服务器费用本地一次推送双触发高希望兼顾便利与可控的用户

2. 仓库、分支与GitHub Pages:最容易翻车的第一道关

很多人以为GitHub Pages的坑在部署环节,实际上配置仓库和分支才是重灾区。这里把几个高频问题拆开讲。

2.1 仓库命名规则不是小事

GitHub Pages有两种站点:项目站点和用户站点。用户站点要求仓库名必须是你的用户名.github.io,这个不做的话,Pages服务根本不会把仓库识别为站点源码。

我第一次建站时忽略了这个问题,仓库名用了类似my-blog这种名字,结果设置里死活找不到Pages选项。换成用户名.github.io之后一切正常。另外说一下,项目站点虽然可以用任意仓库名,但访问路径会变成用户名.github.io/仓库名/,这就牵扯到路径配置问题,Hexo的_config.yml里需要把url和root都改成对应路径,很多文章里的链接404往往就是root没配对。

2.2 源文件和发布文件的分支策略

Hexo项目目录里,真正要推到GitHub的是两类东西:一类是源文件,包括source/、scaffolds/、_config.yml、package.json这些;另一类是构建产物,就是public/目录下由hexo generate生成的文件。

这两类不能混在一个分支里。如果直接把public/提交到主分支,每次重新生成时旧文件不会被自动清理,过几轮提交仓库就膨胀得没法看。

我最终的分支方案是:

  • main分支:存放Hexo源文件,用于多设备同步和备份
  • gh-pages分支:存放public/构建产物,GitHub Pages从该分支发布

本地操作流程是:

hexo clean && hexo generate # 部署到gh-pages分支 hexo deploy

在_config.yml里对应的配置是:

deploy: type: git repo: git@github.com:你的用户名/你的用户名.github.io.git branch: gh-pages

提示:配置hexo-deployer-git插件时,建议用SSH地址而不建议用HTTPS地址,否则每次推送都要输入账密,自动化部署时容易卡住。

2.3 源文件分支被污染的解决思路

还有一个常见问题是,有人把源文件直接放在用户站点仓库的主分支,构建产物也往同一个仓库里推,时间久了分支历史变得很混乱。我自己采用的更稳妥做法是:源文件放在一个私有仓库blog-source,构建产物推送到用户名.github.io的gh-pages分支。这样源文件的访问权限可以私有,公开的只是构建后的静态文件,内容和代码都安全。

3. 云服务器部署链路:从Git裸仓库到自动发布

服务器端部署的核心思路和GitHub Pages类似,也是基于Git的“推一下就更新”模式,但实现方式稍有不同。

3.1 服务器端的Git裸仓库

首先需要在服务器上建一个裸仓库(bare repository),裸仓库不保存工作区的实际文件,只保存Git版本数据,专门充当“接收端”:

mkdir -p /home/git/blog.git cd /home/git/blog.git git init --bare

裸仓库建好后,本地推送的入口就有了。但推送进来的文件还只是Git对象,需要有一个“检查点”把文件释放到真正的Web目录,这就是post-receive钩子的工作。

3.2 post-receive钩子:自动发布的关键

在裸仓库的hooks/目录下创建post-receive文件,内容如下:

#!/bin/bash GIT_WORK_TREE=/var/www/blog git checkout -f

解释一下原理:post-receive脚本会在服务器成功接收推送后自动执行,GIT_WORK_TREE指定了把文件释放到哪个目录,checkout -f强制检出最新内容。这行命令就是把推送来的构建产物直接铺到Web服务目录。

写完脚本后必须赋予执行权限:

chmod +x hooks/post-receive

3.3 两种推送到服务器的策略对比

实际部署过程中,我发现有两条路可以走,各有各的适用场景:

方案A:本地构建,推送静态文件到服务器

本地执行hexo generate生成public/目录,然后把public/作为内容推送到服务器的裸仓库。优点是服务器不需要安装Node环境,占用的系统资源也少,缺点是本地的构建环境必须和发布状态保持一致。

方案B:推送源文件,在服务器上构建

把Hexo源文件推送到服务器,服务器上装好Node和Hexo,收到推送后执行hexo generate再发布。缺点是每次构建都得在服务器上跑一遍,耗时和资源占用都明显提升。

我目前用的是方案A。操作方式是添加一个remote指向服务器仓库:

git remote add server git@服务器IP:blog.git git subtree push --prefix public server main

subtree push会把public/目录单独推送出去,比反复切分支再推送要直观。

3.4 一个容易忽略的权限坑

/var/www/blog这个目录的属主很关键。Nginx默认以www-data用户运行,如果目录归属是root,Nginx会因为没有读权限返回403。而Git钩子又通常以登录用户身份执行,这就导致“文件推送成功了,但网站打不开”的诡异现象。

解决办法是统一权限:

chown -R www-data:www-data /var/www/blog

或者在部署目录上做用户组管理,把执行Git的用户和Nginx用户放进同一组,确保文件对Web服务可读。

3.5 连接与密钥配置

整个部署链路中,本地到服务器的SSH连接建议配置密钥认证。生成密钥对之后,把公钥加到服务器上,这样推送到服务器就不用每次输入密码了。密钥配置的细节不展开说了,很多教程都有,记得把私钥保护好就行。

4. Nginx站点配置与HTTPS证书:访问体验的最后一公里

文件已经到了服务器,接下来就是让用户能访问到。这里才是很多人“部署完打不开”的真正原因。

4.1 最常见的Nginx路径错误

部署完第一次用IP访问时,我遇到的是503或403错误。排查下来的典型原因是Nginx配置里的root路径和站点文件的真实路径没有对上。

假设你的站点文件在/var/www/blog,那么Nginx配置里的关键行应该是:

server { listen 80; server_name yourdomain.com; root /var/www/blog; index index.html; }

注意,如果站点文件实际存放在/var/www/blog/public/,而配置里写的是/var/www/blog,就会找不到index.html。这类问题排查起来很费眼神,建议在服务器上先直接看文件路径,再对照Nginx配置逐项核对。

4.2 缓存策略和压缩配置

静态博客的访问性能优化,主要靠两个参数:缓存和压缩。Nginx里可以这样配置:

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff2?)$ { expires 30d; add_header Cache-Control "public, max-age=2592000, immutable"; } gzip on; gzip_types text/css application/javascript application/json image/svg+xml; gzip_min_length 1k;

静态资源的不可变缓存,能在第二次访问时大幅减少请求量。这里有个小的注意点:如果你更新了同名文件但内容变了,用户的浏览器可能因为缓存读旧版本。所以发布时最好让Hexo生成带哈希值的文件名,或者手动在文件内容上做调整。

4.3 HTTPS证书的申请与强制跳转

现在HTTPS已经是默认选项了。证书申请我用的是自动续期的方案,大概只需要两步:第一步安装证书申请工具,第二步配置证书签发的验证方式。申请下来的证书在Nginx里加载:

server { listen 443 ssl; server_name yourdomain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/private.key; }

同时把HTTP请求强制跳转到HTTPS:

server { listen 80; server_name yourdomain.com; return 301 https://$host$request_uri; }

4.4 DNS解析生效的等待期

在一个坑上等了好久:域名解析配置完成后,访问还是打不开站。查了一圈,发现是本地DNS缓存的问题——不同网络环境下DNS生效速度差异很大,有的几分钟,有的几小时。遇到这种情况别急着改配置,先换个网络试试,比如手机浏览器关掉WiFi用流量访问,如果正常就是本地网络解析没刷新的问题。

5. 写作流程中的日常坑与提效工具

站点部署好了之后,日常维护的重点就变成了写作流程是否顺畅。这部分分享一些我实际用下来很舒服的做法。

5.1 一套已经跑顺的日常写作流程

我现在发一篇文章的流程大概是:

# 1. 创建文章草稿 hexo new post "文章标题" # 2. 编辑 Markdown 文件 vim source/_posts/文章标题.md # 3. 本地预览 hexo server --draft # 4. 生成并发布 hexo clean && hexo generate git add . && git commit -m "new post" git push origin main git push server main

从写草稿到线上更新,整个过程没有一步需要手动上传文件,所有环节都通过Git串联起来。特别是git push server main这一步,把本地构建好的文件同步到服务器,线上马上就能看到。

5.2 图片处理的两个方案

写文章不可避免要插图。我一开始直接把图片放在source/images/目录下,然后用相对路径引用。问题是文章多了之后图片堆积,管理会变得麻烦。后来改成了把图片上传到对象存储,引用时直接用完整URL。两种方案各有适用场景:如果在意数据可控性和长期保存,本地目录更稳定;如果在意页面加载速度和省服务器带宽,图床或对象存储更好。

5.3 我墙裂推荐的几个Hexo插件

以下插件是我目前还在用的,都是实用性很强的:

插件功能使用感受
hexo-generator-sitemap生成sitemap对搜索引擎收录有直接帮助
hexo-generator-feed生成RSS订阅适合维护固定读者群
hexo-deployer-gitGit部署构建后自动推到仓库
hexo-related-posts相关文章推荐提升站内点击率
hexo-wordcount字数统计文章页展示阅读时长

安装插件的方式统一是npm install 插件名 --save,然后在_config.yml里配置。还得提醒一句,Hexo升级版本后部分插件可能不兼容,出现构建报错时优先检查插件版本和Hexo版本的对应关系。

5.4 多设备写文章的同步方案

如果既想在办公室电脑写作,又想在笔记本上继续改稿,源文件的同步就很重要。我的做法是把Hexo源文件推到GitHub私有仓库,换设备时git clone下来,装好依赖就能继续写。注意这时候public/目录不要提交,它是构建产物,每台设备本地生成即可。

6. 这些坑背后的通用思路

踩了足够多的坑之后,回头看就发现,所有坑其实都集中在一个共同的根因上:没有分清“源文件”“构建产物”“部署环境”“Web服务环境”这四层之间的关系。

6.1 把部署这件事拆得更清楚

静态博客部署的本质可以概括为三个阶段:

  1. 构建:Markdown源文件 -> 静态HTML
  2. 传输:构建产物推送到托管位置
  3. 服务:Web服务器把文件正确呈现给访客

很多人在第一步和第二步之间搞混了。比如有朋友问“为什么我改完文章,服务器上的站点没变”,结果一看,他把源文件推送到了服务器,服务器上根本没装Hexo,当然不会自动生成新页面。这就是没有区分“推送源文件”和“推送构建产物”导致的。

6.2 一个排错方法论:每次只改一处

回头看所有排查过程中最值钱的教训是:每次只改动一个变量,然后验证。改完Nginx配置发现还是不行,又去改SSL证书,又去改文件权限,结果问题依然存在,你根本不知道问题到底是哪一个环节引起的。

正确做法是先把问题范围缩小。比如站点打不开,先确认服务器进程还活着,再确认文件是不是真的在对应目录,再确认Nginx配置里的路径和目录权限,一层层往后排查。每次只动一个地方,验证有效再进入下一步。

6.3 回滚:部署之后必须留的退路

只要部署流程跑起来了,就一定会遇到“发布了一版有问题的内容”的情况。所以我在流程里固定下来了两个回滚手段:

  • 在GitHub仓库里保留历史提交,需要回滚时git revert或直接切到上一版提交重新部署
  • 服务器上的/var/www/blog目录里,确保有上一版的备份文件或者至少Git历史完整

有这层保障之后,发布新版本的心态会稳很多:就算线上出问题,回到上一版本只是几分钟的事。

7. 部署完成后的进阶优化思路

基本的部署链路跑通之后,还有一些零碎的优化点值得记录。

7.1 搜索引擎收录的入口配置

新站点刚上线时搜索引擎收录很慢,这是正常现象。我做了几件事加快了这个过程:提交sitemap、配置robots.txt、在文章内做好内链。另外保持稳定的更新频率也很重要,搜索引擎对持续更新的站点有偏好。

7.2 评论功能与站点统计

Hexo是静态博客,没有自带的评论系统和访问统计。我用的方案是接入第三方评论系统,在主题配置里填入对应的参数就行。统计方面则用开源的访问统计工具,在页面上插入一段脚本,数据存在自己手里。这两块配置起来都不复杂,但能显著提升站点完整度。

7.3 主题定制的一点心得

推荐使用成熟主题而不是从零开发,省下的时间都值得。选定主题后,先在本地把基本参数配置好,再逐步调整样式。直接改主题模板文件很方便,但要注意:主题更新时会覆盖自定义修改,所以重要改动最好放到Hexo的source/_data/目录下或者在主题配置里覆盖。

博客搭好之后,最大的变化是写作变成了一种“顺手”的动作。打开终端,新建文章,写内容,保存发布,整个过程不再有和机器搏斗的挫败感——这大概就是所有折腾的意义。希望这篇踩坑记录,能让你少走几次弯路,把时间真正留给写作本身。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询