用Jekyll和GitHub Pages搭建个人博客:零成本、零维护的静态博客方案
如果你正在纠结怎么搭一个自己的博客又不想花钱买服务器、不想折腾数据库、不想天天担心安全问题那 Jekyll 加 GitHub Pages 这套组合应该是你能找到的最省心的方案之一。我自己的博客就是用这套东西跑起来的从第一次提交到正式能访问前后也就一个下午的时间。这篇内容我会把整个流程、关键配置、踩过的坑全部摊开讲包括很多人纠结的“Jekyll 和 Hexo 到底选哪个”这个问题也会给出我的判断。这篇内容适合两类人一类是完全没接触过静态博客的小白想低成本拥有一个个人站点另一类是已经用 Hexo 或其他工具搭过博客但对 Jekyll 和 GitHub Pages 这套原生方案感兴趣、想迁移或对比的人。我会从环境准备讲到域名绑定全程带命令和配置示例你照着做基本就能跑起来。1. 内容整体设计与思路拆解1.1 我对个人博客的基本判断从需求反推方案在动手之前先想清楚一个问题你要的博客到底是给谁看的要承担什么功能如果只是想要一个写技术笔记、生活记录、作品展示的站点那它的核心需求其实只有三个能写内容、能发布、能长期稳定访问。剩下的什么评论系统、访问统计、搜索、标签分类都属于锦上添花早期完全可以不装。按照这个需求去反推方案你会发现很多传统建站方式的性价比很低。买一台云服务器你得配置 Nginx、装数据库、处理 HTTPS 证书、定期打系统补丁万一被攻击还得收拾烂摊子。用动态博客框架比如 WordPress功能确实强大但维护成本也跟着上来了。这些对一个小博客来说都属于过度投资。静态博客方案的优势恰好在这里。它把内容预渲染成纯 HTML 文件发布的时候直接把文件丢到 CDN 上不需要服务端动态执行代码。GitHub Pages 本身就是干这个的它免费托管静态页面还自带 HTTPS全球访问速度也还不错。你唯一要做的就是本地用 Jekyll 把 Markdown 文章渲染成 HTML然后推送到 GitHub 仓库剩下的构建和发布全自动完成。1.2 零成本与长期维护才是最关键的隐藏成本很多人搭博客的时候只看“搭建当天”的成本忽略了“运行三年”的成本。服务器续费一年几百块域名续费一年几十块看起来都不贵但真正贵的是时间系统出问题了你要排查被攻击了你要处理证书过期了你要续。这些小事情单看不难积累起来非常消磨写作热情。Jekyll 加 GitHub Pages 这套方案长期维护成本趋近于零。GitHub 免费托管HTTPS 自动配好不用担心流量攻击就算我这篇文章发了三五年只要 GitHub 这个平台还在它就还在跑。我自己博客运行这几年真正花在“维护”上的时间加起来不会超过一天大部分精力都可以放在写内容本身。另外还有一个容易忽略的优势内容可控。Jekyll 的文章就是纯 Markdown 文件不依赖数据库。哪怕哪一天 GitHub Pages 不用了我也可以把这些文件原封不动搬到任何其他静态托管平台五分钟迁移完毕。这种“数据在自己手里”的安全感用久了才知道多重要。2. jekyll和hexo到底怎么选2.1 两个工具的家底和脾气Jekyll 和 Hexo 是目前最主流的两个静态博客生成器网上关于“jekyll和hexo哪个好”的讨论从来没停过。先别急着站队看清楚它们的底细再下结论。Jekyll 是 Ruby 社区的作品GitHub 的创始人 Tom Preston-Werner 写的2013 年就被 GitHub Pages 官方支持。它的核心卖点和 GitHub 深度绑定你推一个 Markdown 文件上去GitHub 自动帮你构建发布整个过程不需要额外配置。Hexo 是 Node.js 生态的工具国内社区非常活跃。它的特点是速度快、主题风格更现代而且因为中文资料多、文档也友好很多国内开发者第一次搭博客用的就是它。Hexo 通常配合 Travis CI 或者 GitHub Actions 做自动部署本地生成静态文件后推送到仓库的 gh-pages 分支。两个工具都能完成“写文章、生成静态页面、托管到 GitHub Pages”这个核心链路风格差异大于能力差异。就像做同一道菜一个用的是砂锅一个用的是铁锅最后的味道各有千秋但都能吃饱。2.2 我最终选Jekyll的三个理由我第一次搭博客的时候其实也纠结过这个问题。当时我花了一个晚上分别用 Jekyll 和 Hexo 搭了两个demo最后选了 Jekyll核心原因有三个。第一GitHub Pages 原生支持 Jekyll不需要额外的 CI 流程。用 Hexo 的话本地生成 public 目录之后还得推送到专门的分支或者配置 GitHub Actions 来实现自动化用 Jekyll 加 GitHub Pages我只需要把源文件推到主分支平台自动完成构建。少一个环节就少一个出错的地方。第二Jekyll 的模板语言 Liquid 虽然上手有点门槛但它的数据文件机制很适合博客这种内容结构相对固定的场景。后面想加个相册页、读书清单页直接用 YAML 数据文件就能实现不需要额外开发。第三Hexo 换主题的时候经常要关注 Node 版本兼容问题node_modules 一换版本就容易出幺蛾子Jekyll 的主题是基于 gem 的版本锁定相对简单很少遇到依赖地狱。2.3 什么情况下你应该考虑Hexo不能说 Jekyll 一定比 Hexo 好每个工具都有适合它的场景。如果你是前端开发者日常就是跟 Node.js 打交道那 Hexo 的学习成本对你来说几乎为零ejs 模板你本来就会写主题二次开发更顺手。如果你对博客的颜值要求特别高喜欢那种卡片式、瀑布流式的现代风格主题Hexo 的主题生态在这方面确实更丰富。我用过一段时间 Hexo 的 Next 主题颜值和交互手感都很棒这一点 Jekyll 的主题相对朴素一些。我的建议很简单你在哪个技术生态里待得久就选哪个。不要因为单纯比较性能参数而倒向某一方毕竟博客的瓶颈从来不在生成速度上。如果你完全是个新手哪个都不想深入了解那就直接选 Jekyll因为它的部署链路最短碰到问题的概率最少。3. 环境准备与本地搭建3.1 先把Ruby环境装明白Jekyll 是 Ruby 写的所以第一步是装 Ruby。不同系统的安装方式不太一样我把自己试过的整理一下。macOS 用户要注意系统自带的 Ruby 版本往往偏旧而且直接往系统环境里装 gem 容易碰权限问题不建议动系统 Ruby。最简单的方式是用 Homebrew 安装一个独立的 Rubybrew install ruby安装完成后需要把路径配置好。在~/.zshrc里加上这句export PATH/opt/homebrew/opt/ruby/bin:$PATH然后执行source ~/.zshrc让它生效再用ruby -v验证一下。如果版本号是新装的那个就没问题了。Windows 用户建议直接用 RubyInstaller记得要选带 DevKit 的那个版本。安装的时候勾选“Add Ruby executables to your PATH”后面操作会省很多事情。装完 DevKit 之后还要在命令行里跑一次ridk install选择安装 MSYS2 组件这一步是让本地编译扩展工具时能正常工作很多新手在这里卡住其实只要把提示的选项都装上就行。Linux 用户直接走 apt 或者 yum 安装ruby-full和build-essential即可版本一般不会太新但也够用。装好 Ruby 之后把 Jekyll 和 Bundler 一起装上gem install jekyll bundlerBundler 是 Ruby 的依赖管理工具它的作用类似于 Node 生态里的 npm。后面管理 Jekyll 的插件和主题都靠它这一步建议务必装好。3.2 创建第一个Jekyll站点环境就绪之后直接用 Jekyll 自带的命令创建新站点jekyll new my-blog cd my-blog bundle exec jekyll serve打开浏览器访问http://localhost:4000看到默认的欢迎页面你的第一个 Jekyll 站点就起来了。这里有两个值得说明的细节。第一bundle exec前缀不是可有可无的。它保证当前环境下运行的是 Gemfile 里锁定的版本而不是系统全局装的版本可以避免很多版本冲突问题。第二jekyll serve默认带文件监听功能本地改完 Markdown 文件刷新浏览器就能看到效果不需要手动重启。这个开发体验非常舒服也是我很喜欢的点。3.3 目录结构与模板引擎速览Jekyll 项目跑起来之后你会看到这样一个目录结构my-blog/ ├── _config.yml ├── _drafts/ ├── _includes/ ├── _layouts/ ├── _posts/ ├── _sass/ ├── assets/ ├── Gemfile └── index.md每个目录各司其职我用自己的理解给你梳理一下_config.yml是全局配置文件站点的标题、描述、URL、主题都在这里设置是整个博客的中枢。_posts是文章目录文件名必须遵循年-月-日-标题.md的格式这是 Jekyll 识别文章日期的关键。_layouts是页面模板默认有 home、page、post 三个基础模板你可以理解为 HTML 骨架。_includes是公共组件比如页头、页脚、导航栏方便复用。文章头部那段被两条---包围的 YAML 区域叫 Front Matter它定义文章的元信息比如标题、日期、标签、分类--- layout: post title: 我的第一篇文章 date: 2024-01-01 12:00:00 0800 categories: blog tags: [随笔] ---Jekyll 底层的模板语言是 Liquid它跟 Python 社区常用的 Jinja2 有点像控制结构也是{% if %}、{% for %}这种标记语法。设计模板的时候需要写一些逻辑但大部分情况下你只需要改改样式不需要从零写模板。4. 关键配置与写文章流程4.1 _config.yml最容易踩的url和baseurl很多人第一次把 Jekyll 站点推到 GitHub Pages 之后发现样式全丢了、图片全裂了绝大多数情况都是_config.yml里的url和baseurl配置有问题。简单解释一下这两个参数url是你站点的完整域名baseurl是站点部署在域名下的子路径。GitHub Pages 的项目站点访问地址是https://用户名.github.io/仓库名/这时候baseurl必须设置为/仓库名用户站点的访问地址是https://用户名.github.io/baseurl留空即可。如果baseurl配置错误写src/assets/style.css这种根路径引用就会指错位置页面自然没有样式。正确做法是在模板里用{% raw %}{{ site.baseurl }}{% endraw %}拼路径比如link relstylesheet href{% raw %}{{ site.baseurl }}{% endraw %}/assets/style.css本地调试的时候url可以先填http://localhost:4000正式发布前再改成线上域名。我之前有一次忘了改本地一切正常一上线上所有文章里的链接全是 localhost排查了半天才发现问题。4.2 写文章的正确姿势Front Matter与MarkdownJekyll 写文章用的是 Markdown这个大家基本都会。但初学者容易忽略的是 Front Matter也就是文章开头那段 YAML 信息。没有 Front Matter 的 Markdown 文件Jekyll 不会当做文章处理也就不会被渲染成 HTML 页面。最简的 Front Matter 只需要一个属性--- layout: post ---但实际写作的时候我建议把title、date、author、tags都写上。这些信息可以用来做文章列表的分组和筛选后面想做分类页、标签页的时候就不用回头补数据了。我的习惯是每篇文章至少带上tags这比单纯的分类更灵活一篇跨领域的文章可以打多个标签检索的时候很方便。写作还有一个容易被忽略的点文件名里的日期是 Jekyll 识别文章发布时间的核心依据Front Matter 里的date字段反而是辅助性的。所以你如果手动建文件文件名一定要按2024-01-01-标题.md的格式写否则文章不会出现在正确的时间轴上。4.3 本地预览与内容调试写文章的过程中我习惯开着本地服务实时预览。jekyll serve的--drafts参数可以预览草稿草稿文件放在_drafts目录下不会被正式发布bundle exec jekyll serve --drafts这里有个小技巧本地草稿里的图片路径最好一律使用相对路径写比如../assets/images/xxx.png而不是写死http://localhost:4000/assets/xxx.png。否则文章发布到线上之后图片链接还是指向本地地址全部会裂。我自己吃过这个亏后来就把所有图片引用全部改成相对路径一劳永逸。本地预览还有一个作用就是提前暴露 Markdown 渲染问题。Jekyll 默认用 Kramdown 做 Markdown 解析它和 Typora 这类编辑器渲染出来的效果会有细微差别。例如Kramdown 的 Markdown 内就不支持某些原始 HTML 标签的嵌套解析如果你文章里嵌了不少花式 HTML本地预览一步能帮你提前发现省得线上出问题再改。4.4 换主题远程主题与本地主题Jekyll 默认主题叫 Minima结构干净但确实寡淡。想换主题有两条路。路由一远程主题。GitHub Pages 支持jekyll-remote-theme插件可以在_config.yml里直接指定一个 GitHub 仓库作为主题来源remote_theme: username/repo-name用远程主题的好处是主题和你的文章分离主题更新了直接在 GitHub 仓库releases里跟踪。不过也有局限GitHub Pages 只允许白名单内的插件主题里如果引入了白名单之外的插件构建会直接失败。路由二本地主题。把主题的源文件整体下载到自己的仓库放在_layouts、_includes、assets这些目录里相当于完全接管了主题的代码。这样做的好处是任何效果都能自己改坏处是主题官方更新的时候你自己改过的文件会有冲突合并起来费劲。对于大多数人我的建议是先玩远程主题等确认某个主题自己确实要长期用了再把它的自定义修改固定下来。我自己现在用的是远程主题加上两三个自定义布局文件既保证主体功能跟着上游走又能在局部作出自己的风格。5. 一键发布到GitHub Pages5.1 创建仓库与分支策略到这一步本地博客已经能跑了接下来就是把内容搬到线上。先去 GitHub 新建一个仓库名字有个讲究如果你想拥有的是https://用户名.github.io这样的用户站地址仓库名必须精确地叫用户名.github.io如果名字起别的比如my-blog那最终地址会变成https://用户名.github.io/my-blog也就是项目站点。这两种方式我都试过。用户站点的地址干净好记适合当个人主页用项目站点的好处是一个账号下面可以挂多个不同的项目页。如果你只是想有一个个人博客直接用用户站点方式最省事。仓库建好之后我习惯把主分支名称设置成main因为 GitHub Pages 构建的默认分支一般就是它。分支策略上源文件和构建产物不需要分家因为 GitHub Pages 自己会读 Jekyll 源文件来构建这也正是 Jekyll 方案最省心的地方。5.2 让GitHub Pages自动构建仓库建好之后把本地内容推上去git init git add . git commit -m first commit git branch -M main git remote add origin https://github.com/你的用户名/你的仓库名.git git push -u origin main推送完成后到仓库的 Settings 页面找到 Pages 选项在 Source 里选择 Deploy from a branch分支选main目录选/ (root)保存即可。保存后等一两分钟GitHub Actions 会自动触发构建。你可以在仓库的 Actions 标签页看到构建日志等状态变成绿色访问https://你的用户名.github.io你的博客就在线了。这里有个我踩过的坑GitHub Pages 构建 Jekyll 所用的版本和插件列表跟本地环境不一定完全一致。所以本地能用不代表线上就能构建成功。要避免这个问题最稳妥的方式是本地也使用 GitHub 提供的github-pagesgem 来管理依赖。在你的 Gemfile 里改成这样source https://rubygems.org gem github-pages, group: :jekyll_plugins改完之后本地执行bundle install这样本地用的就是和线上完全一致的 Jekyll 环境。这个细节能帮你省去九成以上的线上构建问题。5.3 绑定自定义域名GitHub Pages 默认的域名是用户名.github.io如果你有自己的域名可以在 GitHub Pages 设置页面里填入域名GitHub 会自动帮你配置 HTTPS。绑定的步骤我拆开讲一下。首先在域名服务商那边增加一条 CNAME 记录把www指向用户名.github.io。如果你想让根域名比如example.com也能访问还需要在 DNS 解析里加 A 记录指向 GitHub Pages 的 IP 地址。GitHub 官网文档里给出的 IP 是185.199.108.153、185.199.109.153、185.199.110.153、185.199.111.153不同地区的访问建议配置多条 A 记录。CNAME 记录配置完之后还有一步容易被忽略在 Jekyll 项目的根目录下放一个名为CNAME的文件里面写你的域名比如example.com。这个文件会跟着仓库一起提交确保以后重新配置或者构建的时候域名不会丢。HTTPS 证书是 GitHub 自动签发的配置好域名之后等一段时间Settings 页面里会显示 Enforce HTTPS 的选项把那个按钮打开整个站点就全程走加密连接了。我自己在绑定域名的时候遇到过一个情况DNS 配置好了但一直显示证书颁发中等了几个小时才生效。如果遇到类似情况不要急过段时间再看一般都会自动完成。6. 进阶功能与维护技巧6.1 评论系统选配的开源方案博客要跟读者互动评论系统是绕不开的话题。Jekyll 是纯静态页面没有服务端能力所以要借助第三方评论服务。我推荐 giscus它是基于 GitHub Discussions 的评论系统——评论内容会存到你的 GitHub 仓库的 Discussions 里数据完全掌控在自己手上。接入 giscus 的流程很直接先到 GitHub 的仓库 Settings 里开启 Discussions 功能然后到 giscus 官网按提示填仓库名生成一段嵌入脚本放到_includes目录下的评论组件里在文章页模板中引入即可。相比其他闭源评论服务giscus 最大的优势是数据不属于某个第三方平台你的读者留言都沉淀在 GitHub 上哪天想迁移或者导出都很方便不用看任何服务商的脸色。6.2 访问统计不牺牲隐私的轻量方案静态博客无法直接统计访问量但可以用轻量的脚本统计服务。我在用的方案是 GoatCounter它对个人网站免费开放一个简单的 JavaScript 片段就能接入。它不依赖 Cookie隐私政策上可以写得很干净访客也不会被无感追踪。如果你不喜欢把数据放在别人那也可以用 Umami 自托管缺点是得有台云服务器这就违背了“零维护”的初衷。所以我最终选了 GoatCounter数据量虽然不大但能看到每天的访问趋势、来源渠道、热门文章够用了。6.3 搜索与SEO最容易被忽视的体验静态博客没有后端数据库站内搜索想实现得花点心思。如果你文章量不大最简单的方式是直接用浏览器的 CtrlF 当前页搜索再加一个支持站内搜索的第三方搜索服务。Jekyll 生态里也有基于 Lunr.js 的本地搜索方案文章量在几百篇以内都能流畅运行原理是启动时生成一份全部文本的 JSON 索引前端搜索时本地匹配无服务器依赖、无请求费用值得一试。SEO方面Jekyll 先天就做得不错。Markdown 渲染出的 HTML 是语义化的标题层级默认整齐网站地图sitemap.xml可以用插件自动生成Robots.txt 也可以手动维护。基础的 SEO 在 Jekyll 里不用刻意折腾重点放在标题和 meta 描述上就好。6.4 自动化发布写内容就能上线用 Jekyll 加 GitHub Pages 的完整工作流最简单的一种就是——直接在 GitHub 网页端点击 “Add file” 创建带正确文件名的 Markdown 文件推送后 GitHub 自动构建两分钟文章上线。如果平时用本地写作git add、git commit、git push三条命令打完收工。我自己的习惯是本地用 VS Code 写 Markdown配合 Markdown 预览插件写完推送到仓库后GitHub Actions 自动构建几分钟内就能在线上看到新文章。整套流程不依赖任何第三方工具装好环境之后几乎不费心。7. 常见问题与排查记录7.1 GitHub Pages构建失败的常见原因远程构建失败是新手最容易遇到的问题大多数情况都不是代码写错而是环境版本问题。我遇到过的典型场景如下表现象原因解决方案Actions 日志提示You have already activated...本地 gem 版本与线上不一致远程构建时依赖冲突本地 Gemfile 换成github-pagesgem重新bundle install构建日志提示插件不存在用到非白名单插件远程环境不允许安装改用法白名单支持的插件或换实现方案文章列表为空文件名日期格式不对或 Front Matter 缺失检查_posts文件名是否严格为2024-01-01-标题.md页面样式丢失baseurl配置错误静态资源路径拼错修正_config.yml的url和baseurl模板中统一用site.baseurl拼接资源路径排查的核心思路是把线上构建日志当作第一个信息来源。Actions 页面里如果构建红了你先把日志从头到尾看一遍大多数错误信息都是人话定位会比瞎猜快很多。7.2 本地正常线上样式全丢这个问题我前面提到过核心在baseurl。本地预览的时候baseurl是空的资源路径会指向http://localhost:4000/assets/...浏览器能正常加载。发布到线上的项目站点后资源地址变成了https://用户名.github.io/assets/...但实际文件在https://用户名.github.io/仓库名/assets/...路径不匹配样式就全丢了。解决方案就一句话所有引用资源的地方都加上{% raw %}{{ site.baseurl }}{% endraw %}前缀。检查的时候重点看_includes里的head部分_layouts里的页脚和导航以及每篇文章里手写的图片链接。养成写绝对路径前先拼接site.baseurl的习惯就不会再出现这个问题。7.3 本地和线上效果不一致另外一个让很多人抓狂的问题是本地预览和线上效果不一样。原因通常是本地 Jekyll 版本和 GitHub Pages 用的版本不同导致 Markdown 渲染结果有差异少数插件在本地能运行但线上不支持。为了避免这种不一致我强烈建议本地环境直接用github-pagesgem。Gemfile 里写上这一行gem github-pages, group: :jekyll_plugins然后重新bundle install。这样本地跑的 Jekyll 就和线上完全同版本了本地什么样线上就是什么样不存在“本地好好的一上线就坏”的尴尬。7.4 一套实用排查思路如果你遇到本地构建正常但线上失败先把线上 Actions 日志完整贴到搜索引擎或者 Jekyll 官方文档里去查大部分错误都能找到现成解释。如果是样式问题按 F12 打开浏览器开发者工具看 Network 面板里的静态资源请求状态码是 404 还是路径错误一目了然。还有一个小建议刚开始搭建的时候尽量用默认主题跑通全流程再考虑换主题、加插件。默认主题配置最少、坑最少能最快让你把“本地写文章、推送发布、绑定域名”这条主链路走完。主链路顺畅了再去折腾花活心里也有底。我自己第一次搭博客的时候就是死磕主题细节结果前面一路顺风最后却在配置上浪费了一个晚上。写在最后的一点体会从我自己的使用体验来看Jekyll 搭配 GitHub Pages 这套方案最打动我的地方不是它有多炫酷而是它让我把注意力从“搞博客”转移到了“写博客”上。Markdown 记录想法推送命令完成发布没有后台要打理没有数据库要维护也没有账单要担忧。如果你也在找一个低成本、可长期维护、内容完全自主的个人博客方案并且不需要纠结太多功能上的花活那这套组合值得你花一个下午试试。过程中遇到环境问题、配置问题记住一个原则先跑通最简版本再做加法能帮你少走很多弯路。