FEATURED · 精选文章

Hexo部署GitHub Pages常见报错排查与自动化部署指南

发布时间 / 2026/9/19 2:57:11
来源 / 创域科博编辑部
栏目 / 资讯中心
Hexo部署GitHub Pages常见报错排查与自动化部署指南 开头上周末晚上我打算把新写的一篇技术笔记部署到自己的 GitHub Pages 博客上。本地跑hexo d终端输出一串英文日志乍一看像是成功了结果浏览器一刷新大白屏加 404。这不是我第一次在 GitHub 上托管技术博客时踩到报错也不会是最后一次。说句实在话用 GitHub 托管技术博客本质上是和一连串报错打交道的过程。本地环境、git 推送、Pages 构建、域名解析、多终端同步、自动化部署每个环节都可能埋着雷。这些报错单独看都不复杂但架不住它们是连环的今天把推送问题修好了明天又冒出 404后天 CI 构建失败。你需要的不是记住某一条命令而是理解工具链背后的工作原理知道报错信息指向哪里然后顺着链路去排查。这篇文章就是我踩过无数次坑之后的完整记录重点覆盖 Hexo 部署到 GitHub 时的典型报错、排查思路和修复方法同时也涉及 git 推送认证、Pages 生效、自定义域名、多终端协作和 Actions 自动部署等话题。不管你是第一次部署博客还是已经跑起来但偶尔被报错困扰这篇文章应该能帮你省下不少半夜排查的时间。1. 部署前的全局配置与仓库初始化这里省一步后面坑十倍很多人部署博客时第一反应是直接把仓库建好、开始 push却忽略了本地环境里最基础的几个配置项。我见过太多案例问题不在部署环节而是在 git 全局配置和 SSH 密钥上导致推送的时候反复认证、反复失败。所以第一步必须先确保本地环境是干净的、可识别的。1.1 git 全局配置为什么一半的报错能在这里提前排除git 提交时需要用user.name和user.email来标识作者身份。如果你从来没设置过全局配置某些操作会触发提示甚至报错。更关键的是GitHub 服务器识别你是不是仓库的实际提交者靠的是注册邮箱与 SSH 密钥而本地 git 只知道你是谁两者混淆时就会出现权限相关的问题。建议先执行两条命令git config --global user.name 你的名字 git config --global user.email 你注册GitHub的邮箱这个邮箱最好和 GitHub 账号的主要邮箱保持一致。如果你在 GitHub 开启了邮箱隐私保护那就用 GitHub 提供给你的你的用户名users.noreply.github.com这个邮箱否则后续一些提交关联判断可能出现偏差。配置完成后执行git config --global --list查看结果确认没有拼写错误。1.2 SSH 密钥与 HTTPS 两种认证方式的选择从本地推送到 GitHub常用方式有两种HTTPS 和 SSH。HTTPS 方式简单直接但如果你用的是密码而非 Personal Access Token很可能会遇到 Authentication failedSSH 方式需要生成密钥、配置公钥但配置好之后一劳永逸不会出现密码失效的问题。我推荐对博客这类高频推送的场景使用 SSH。生成 SSH 密钥ssh-keygen -t ed25519 -C 你的邮箱一路回车默认保存到~/.ssh/id_ed25519然后把公钥内容加到 GitHub 的 SSH Keys 里。验证是否生效ssh -T gitgithub.com第一次连接会提示确认主机指纹输入yes即可。看到Hi username! Youve successfully authenticated就说明认证通过。如果这一步出现 Permission denied多半是公钥没粘贴完整或者粘贴到了 Deploy Keys 而不是 Authentication Keys这是初学者最容易犯的错。1.3 站在坑里看报错fatal: not a git repository 与 _config.yml 的关系Hexo 博客站点通常由_config.yml配置文件、source目录、themes目录、package.json等组成。部署到 GitHub 的流程是本地先执行hexo generate生成静态页面到public目录再借助hexo-deployer-git插件把public目录内容提交并推送到指定仓库。这个链路里最容易出的问题就是明明在根目录执行了命令却提示fatal: not a git repository。出现这个报错大概率是命令执行目录不对。Hexo 项目的.git目录通常是部署插件在第一次运行时自动初始化的而这个初始化的位置取决于你当前所在的目录。如果你在博客根目录执行hexo d插件会以根目录作为 git 仓库如果你稀里糊涂进入了主题目录那推送就会失败因为主题目录下没有独立的 git 仓库除非主题本身也是 git clone 下来的。另一类原因是_config.yml里 deploy 配置出现了问题。打开博客根目录下的_config.yml找到 deploy 段一个标准的配置长这样deploy: type: git repo: gitgithub.com:username/username.github.io.git branch: mainusername必须替换成你自己的 GitHub 用户名仓库地址必须真实存在branch 要和你远端仓库的分支名匹配。早期教程普遍写master但现在 GitHub 新仓库默认分支是main这一步对不上也会报错。所以配置完之后建议先执行git ls-remote gitgithub.com:username/username.github.io.git手动测一下地址连通性确认远端仓库确实可访问再执行部署能省掉一大半莫名其妙的推送失败。2. fatal 级别的推送报错认证、仓库地址与分支保护排查实录部署环境准备好之后报错的重灾区就是 git 推送环节。这个阶段的错误信息往往很简短、很唬人比如remote: Repository not found或Authentication failed。但如果你理解了 git 推送的完整链路——本地 git 仓库、远端 HTTPS/SSH 地址、服务器端仓库权限、分支保护规则——你就能快速定位问题在哪一环。2.1 remote: Repository not found为什么明明仓库存在却找不到这个报错特别迷惑人因为你在浏览器里明明能打开这个仓库但 git 推送时却提示找不到仓库。我第一次遇到时在浏览器和终端之间反复切换各种怀疑仓库被删了。后来才搞清楚这通常不是仓库不存在而是认证身份与仓库访问权限不匹配。先说 HTTPS 场景。如果你用https://github.com/username/repo.git作为远端地址推送时会弹窗要求输入用户名和密码。现在 GitHub 已经不接受账户密码推送必须使用 Personal Access Token。如果你在密码框里粘贴了 Token 却没给够权限或者 Token 里的repo权限没勾选git 就会报确认不了身份的错误最终表现为Repository not found而不是权限不足。再说 SSH 场景。如果你在同一台机器配置了多个 GitHub 账号比如一个工作号一个个人号而推送仓库的时候恰好加载了错误的 SSH 密钥服务器看到的是一个认证通过的账号但这个账号不是你仓库所在的账号GitHub 为了避免泄露仓库信息不会告诉你你没权限而是直接报仓库不存在。这就是为什么多账号环境下SSH 配置必须明确使用哪把密钥。排查步骤# 查看当前远端地址 git remote -v # 确认当前用的 SSH 密钥 ssh -T gitgithub.com # 手动测试仓库连通性 git ls-remote gitgithub.com:username/repo.git仓库地址写错的概率也很高。username拼写、仓库名大小写、是否多了或少了后缀.git任何一个细节都会导致这个报错。建议直接在浏览器地址栏复制服务器的 SSH 地址而不是凭记忆手敲。另外私有仓库必须确保推送账号有collaborator权限否则也会报 Repository not found。2.2 Authentication failed从 HTTPS 缓存到 Token 的完整修复链路如果报错信息里有Authentication failed或could not read Username问题基本集中在 HTTPS 认证上。我在 macOS 上遇到过一种情况系统钥匙串缓存了一个旧密码导致每次 push 都弹出认证窗口而且填入正确 Token 后依然失败。后来排查发现钥匙串里存的是旧账户密码系统优先读取了那个缓存根本没有把 Token 送到 GitHub。修复的办法# macOS 清除 git 凭证 git credential-osxkeychain erase hostgithub.com protocolhttps [按回车结束] # Windows 打开控制面板 - 凭据管理器 - Windows 凭据删除 github.com 相关条目 # 清除后再推送按提示输入用户名和 TokenToken 生成时勾选repo权限有效期可以设成 90 天或更长。生成之后马上复制保存关闭页面后就再也看不到了。还有一种隐蔽情况你用了git config --global credential.helper store密码文件里存着旧密码即使删了钥匙串git 还是读本地存文件。所以干脆一条命令git config --global --unset credential.helper再重新推送让 git 提示你输入新的用户名和 Token认证通过后可以选择是否重新缓存。2.3 分支名与保护规则push 被拒的另一种可能另一种推送被拒的情况和认证无关是分支层面的问题。报错信息通常是! [rejected] main - main (fetch first) error: failed to push some refs to gitgithub.com:username/repo.git这种在多人协作时很常见说明远端 main 分支上有你本地没有的提交。解决方法是先git pull --rebase拉取远端内容处理可能的冲突后再推送。但如果你用的是自己一个人维护的博客仓库这个提示往往意味着你操作了什么导致仓库历史不一致比如在 GitHub 网页端直接编辑过文件或者换电脑时没有拉取最新代码。还有一类分支保护导致的拒绝报错信息里会带protected branch。这种主要出现在打开了分支保护规则的仓库你推送的账户不一定有管理员权限或者规则要求必须走 Pull Request 才能合入。虽然博主一个人的仓库很少开这个但如果你在 GitHub 网页里配置过 Actions 的自动发布流程有时候保护规则会是默认开启的。确认办法是仓库 Settings - Branches查看 Rule 列表要么调整规则要么换一个可以直接推送的分支。3. 404 不是最后的终点Pages 生效、CNAME 与自定义域名的隐形坑推送成功不代表部署成功。很多人包括我第一次在这一步被卡了很久终端里执行hexo d干净利落git 日志也显示推送完成结果打开username.github.io一看404 白屏。这个阶段的报错不在终端里而在浏览器里属于更隐蔽的一类问题。3.1 打开 GitHub Pages 之后为什么还是 404先确认你的仓库类型。如果你建的是username.github.io仓库GitHub Pages 站点默认用这个仓库的main分支作为发布源如果是其他普通仓库站点地址是username.github.io/repo而且你必须到仓库 Settings - Pages 里手动设置 Source 为 Deploy from a branch 并选定分支。常见的 404 原因有几个。第一Pages 构建还没有完成。GitHub Pages 的构建不是实时的推完代码之后可能要等几十秒到一两分钟页面刷新太快看到 404 很正常。这时候打开仓库的 Actions 标签页查看构建状态如果构建成功但页面还是打不开那大概率是路径问题。第二hexo 的root配置和仓库路径不匹配。站点的浏览器地址是username.github.io/repo那么_config.yml里的root必须写成/repo/否则资源路径全部指向根目录页面加载后 CSS、JS 全部 404看起来就是白屏。用username.github.io仓库则可以保持root: /。这也是很多人明明部署成功却觉得博客坏了的高频原因。第三没有指定 Pages 发布源。我之前遇到过一个情况仓库里有main分支也有gh-pages分支Pages 设置里选错了分支导致发布源一直是个旧版本。每次推送新内容都不见效果排查到最后才发现是发布源没切对。建议打开 Settings - Pages把 Build and deployment 里的 Source 设为 Deploy from a branchBranch 选成你推送的分支。如果上述都排除了我们可以用 curl 看一下 HTTP 状态curl -I https://username.github.io返回 200 说明站点正常返回 404 说明 Pages 并没有把内容发布到这个地址。结合 Actions 日志继续看是 Jekyll 构建失败还是静态文件部署成功。需要注意GitHub Pages 默认走 Jekyll 构建如果你用的是纯静态 HTML 但带了类似_开头的目录Jekyll 会忽略掉这些文件。Hexo 生成的public目录里如果有_next之类的文件夹建议在仓库根目录添加一个空的.nojekyll文件让 Jekyll 构建彻底退出。3.2 CNAME 文件被 hexo clean 清掉自定义域名报错的根因把域名解析到 GitHub Pages 后需要在仓库根目录放一个CNAME文件内容写你的自定义域名比如blog.example.com。没有这个文件GitHub 会认为你没有绑定自定义域名访问自己的域名时就会 404 或者跳转到username.github.io。一个隐蔽的坑是如果你用 hexo 的 deploy 插件推送每次重新生成静态文件时public目录是全新生成的你在仓库根目录手动创建的CNAME文件会被覆盖清理掉。很多人搞不懂为什么第一次设置域名能生效过几天再部署一次就变回username.github.io了。规避办法很直接把CNAME文件放进 hexo 的source目录让它在生成静态页面时自动复制到public根目录source/CNAME 内容: blog.example.com这样每次hexo g hexo d之后CNAME都会被重新送到仓库里不会被冲掉。自定义域名的 HTTPS 证书启用也需要时间。在 Pages 设置里勾选 Enforce HTTPS 之后GitHub 会向证书颁发机构申请证书这个过程通常几分钟到几十分钟不等。如果一直提示证书签发失败多半是 DNS 解析记录设置不完整比如 CNAME 解析到了错误的主机或者你的域名服务商返回了额外记录。DNS 配置要求很简单blog.example.com做一条 CNAME 记录指向username.github.io其他任何 A 记录都不要留避免覆盖掉 CNAME 记录。4. 多终端和 Actions 部署不容易想到的权限与依赖问题当你把博客折腾顺了下一步肯定会遇到换个电脑继续写博客或者直接用 GitHub Actions 自动化部署的需求。这两个方向各有各的报错大部分都不在博客本身的运行逻辑里而是 docker 镜像、依赖安装、子模块、Token 权限这些边缘问题上。4.1 新电脑克隆博客仓库后的报错我在新电脑上 clone 博客仓库后第一件事就是执行npm install结果各种依赖版本冲突hexo 直接起不来。早期我还经常忘记这一点博客仓库本身只是一个源文件仓库必须先有 Hexo CLI 才能执行生成和部署命令。如果你 clone 下来后直接运行hexo s系统会提示hexo: command not found。解决顺序# 先全局安装 hexo-cli npm install -g hexo-cli # 进入博客源码目录安装项目依赖 npm install # 主题如果是 git submodule还需要拉取子模块 git submodule update --init --recursive # 本地验证 hexo g hexo s这一串下来最容易报错的是 submodule 步骤。如果你之前把主题通过git submodule add方式引入新机器 clone 仓库时默认不会拉取子模块内容主题目录是空的生成页面必然失败。当然我后来更推荐直接把主题文件放进博客仓库里把主题目录复制进来删除其中的.git目录推送到自己的仓库。这样整个博客源码是完整自包含的多终端同步时不用依赖子模块维护负担小很多。4.2 GitHub Actions 推送时报权限错误的排查自动部署听起来很省事但 Actions 工作流里推送代码时经常遇到权限错误。报错信息通常包含remote: Permission to username/repo.git denied to github-actions[bot]原因在于工作流里执行 git push 时使用的是GITHUB_TOKEN这个默认令牌。令牌默认只有当前仓库的读取权限要让工作流能推送到本仓库的分支需要在工作流的permissions字段显式声明permissions: contents: write如果你的部署流程是把生成的静态文件推送到同一个仓库的另一个分支比如gh-pages那么permissions.contents: write通常就够用了。如果工作流要跨仓库推送或者推送到受保护分支那GITHUB_TOKEN可能还不够需要用你自己生成的 Personal Access Token 配置为仓库 Secrets然后在工作流里引用- name: Deploy env: DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} run: | git push https://x-access-token:${{ secrets.DEPLOY_TOKEN }}github.com/username/repo.git main这是很多人踩过的坑。如果你是第一次写 GitHub Actions 部署博客我建议先跑一个最简单的工作流只做手动触发确认权限没问题之后再接入推送步骤。关于 Actions 里的依赖安装还有一个很典型的报错npm install 时提示Failed to get response或者超时。这通常是因为 npm 默认源在国外Actions 的虚拟环境网络波动就会失败。解决办法是切换 npm 镜像源但这部分根据你的网络环境来选不展开说了。4.3 我建议的部署方式演进从手动 hexo d 到 Actions 自动发布如果你一直是本地执行hexo d那我建议尽快切换到 GitHub Actions 自动发布。原因很简单减少本地环境的依赖。本地部署意味着每一台电脑都要装 Node.js、装依赖、配 SSH 密钥任何一环出问题都会打断写作节奏。而 Actions 部署你只需要把博客源码推送到仓库工作流自动完成安装、生成、部署三步所有流程都在 GitHub 的虚拟机上跑与本地环境完全隔离。我目前的工作流大概是这样name: Deploy Blog on: push: branches: [main] jobs: build-and-deploy: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkoutv4 with: submodules: recursive - uses: actions/setup-nodev4 with: node-version: 20 - run: npm install - run: npx hexo generate - run: npx hexo deploy如果你用 hexo-deployer-git 插件npx hexo deploy会读取_config.yml里的 deploy 配置推送到指定分支。这时 Actions 的GITHUB_TOKEN需要有 contents write 权限否则最后一步会被拒绝。另一种更直接的方式是使用peaceiris/actions-gh-pages这个第三方 Action它专门处理静态站点部署到 gh-pages 分支比较省心。把部署搬到 Actions 之后本地环境只需要维护博客源码和写作环境不再需要担心 SSH 密钥、node 版本这些问题。出错了直接看 Actions 日志比本地终端满屏报错容易定位得多。结尾最后说点个人体会。托管技术博客这么多年踩过的报错少说也有几十种但回头看每一个报错都让我多理解了一层工具链的工作原理。GitHub Pages 之所以初学者觉得难搞是因为它把 git、静态站点构建、DNS 解析、CI/CD 好几个领域拧在一起任何一环的知识盲区都会表现为一个莫名其妙的错误。但只要把链路理清楚报错就会变得非常友善——它其实在告诉你具体是哪一环出了问题。如果让我给一个最实在的建议一开始就用 Actions 自动部署并且把 CNAME 文件和主题都收进博客源码仓库。这两个决定能帮你避开后续 80% 的环境类问题。剩下的报错照着日志一步一步来基本都能解决。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻