使用 Astro 构建并部署个人博客到 GitHub Pages 全流程指南
作为开发者,我们很多人都希望拥有一个属于自己的博客。它不仅可以记录成长历程、展示作品,还能帮助我们梳理和积累知识。我自己也一直在维护博客,并在过程中不断学习新技术。最近我尝试了一个新兴的静态站点生成器 Astro,并成功将博客部署到了 GitHub Pages。这篇文章将一步步分享整个流程,包括 Astro 项目创建、路径配置、图片处理、分支推送、Token 设置等关键细节,希望对你有所帮助。
1. 为什么选择 Astro?
市面上有很多静态网站生成器(SSG),例如 Hexo、Hugo、11ty 等等。而 Astro 是近年来备受关注的一款,原因包括:
- 极快的加载速度:Astro 默认采用静态渲染,生成纯静态的页面,没有多余的前端负担。这意味着用户打开你的博客会非常迅速,体验流畅。
- 框架无关且兼容多框架:Astro 并不局限于某个前端框架。你可以在 Astro 项目中混用 React、Vue、Svelte 等组件 。对于熟悉这些框架的前端开发者来说,迁移成本低,想用哪种组件就用哪种。
- 支持 Markdown 和 MDX:写博客离不开 Markdown。Astro 原生支持 Markdown,同时也支持 MDX(在 Markdown 中使用JSX组件)。这意味着你可以一边用简洁的 Markdown 写作,一边插入动态组件丰富内容,灵活性很高。
- 部署简单、生态活跃:Astro 的社区非常活跃,新手遇到问题可以很容易地找到资源和解答。而且部署非常方便,官方文档提供了多种部署方案。对于我们来说,省心是很重要的因素。
相比一些老牌的静态站点工具,Astro 更现代化、更易上手,也更符合当前前端的发展趋势 。如果你想尝试新的技术栈,Astro 值得一试。
2. 创建 Astro 博客项目
在开始之前,请确保你已经安装了 Node.js(建议版本 ≥ 18,以满足 Astro 的要求)。现在,我们使用官方脚手架命令来创建项目:
npm create astro@latest
执行上述命令后,Astro 会启动交互式向导,引导你初始化项目。按照提示选择项目模板,这里我们选择 Blog 模板(博客模版),以便快速生成一个带有示例文章和布局的博客框架 。脚手架会自动下载所需文件并安装依赖(如果没有自动安装,记得进入项目目录后运行 npm install)。
依赖安装完成后,在项目目录运行开发服务器:
npm run dev
Astro 启动本地服务的默认端口是 4321,你可以打开浏览器访问 http://localhost:4321 查看博客的预览效果 。如果一切顺利,你现在应该可以看到一个默认的 Astro 博客站点在本地跑起来了。
小提示:如果 4321 端口被占用了,Astro 会尝试使用下一个可用端口。终端会打印实际使用的地址,请以终端输出为准。
现在,我们已经成功创建了 Astro 项目并在本地运行起来,接下来就可以根据需要自定义博客内容了。
3. 自定义页面内容
Astro 的博客模板已经为我们生成了基本的页面和示例内容。你可以根据个人需求,对这些页面进行修改或新增页面。常见的定制包括:
- 首页内容:编辑 src/pages/index.astro 文件,修改首页显示的内容。
- 关于页面:编辑 src/pages/about.astro(或将其改名为 about.html.astro,详见下节)来自定义“关于我”页面的信息。
- 博客文章:在 src/content/blog/ 目录下撰写或添加 Markdown/MDX 文件来发布文章。
一般来说,博客模板会附带几篇示例博文。你可以参考它们的格式,然后撰写自己的文章:文件放在 src/content/blog/ 下,使用 markdown 前置 YAML (---) 来写标题、日期等元信息,内容用 Markdown 语法书写。Astro 会在构建时把这些文章生成静态页面。
下面是一个关于页面的示例代码片段,使用 Astro 组件和 Markdown 语法编写:
3.1 关于我页面示例(About Me)
<Layout
title="About Me" <!-- 页面标题,将会显示在页面头部或标签页标题 -->
description="Software Engineer & AI Explorer" <!-- 页面描述,有利于SEO -->
pubDate="2025-04-02" <!-- 发布日期 -->
heroImage="/blog-placeholder-about.jpg" <!-- 页眉背景图(从 public/ 文件夹引用) -->
>
<p>
Hi! I’m Yuxu Ge, a software engineer and AI enthusiast with 10+ years of backend experience.
I'm currently pursuing an MSc in AI, exploring LLMs, RAG, agents and virtual intelligence.
</p>
</Layout>
上面这段代码定义了一个关于我的页面。在 Astro 中,页面组件可以使用类似 HTML + JSX 的语法。这里我们利用博客模板提供的
3.2 生成 /about.html而不是/about/index.html
默认情况下,Astro 会将每个页面生成为一个文件夹形式,比如 about.astro 会输出为 dist/about/index.html(访问时对应 /about/ 路径)。如果你更希望关于页的路径直接是 /about.html(这样访问路径中会带有文件名),可以通过重命名页面文件实现:
src/pages/about.html.astro
将页面文件名改为以上格式,Astro 构建时就会直接生成 dist/about.html 文件 。简单来说,在文件名中加入 .html 可以让 Astro 输出独立的 HTML 文件而非目录。这纯粹是路径风格的选择问题:两种方式都能正常访问页面,使用哪种取决于你的喜好。
完成内容定制后,你的博客在本地看起来应该更有个人特色了。不过,光有本地页面还不够,我们还需要考虑图片资源和部署等问题。下一节先来看看在 Astro 中如何正确地处理 Markdown 文档中的图片资源。
4. 正确处理 Markdown 中的图片
写技术博客往往需要插入图片(如截图、示意图等)。在 Astro 中,如果图片路径处理不当,可能会出现开发时能看到图片但部署后图片丢失的问题。Astro 官方给出了推荐的解决方案:
- 将图片放入 public/ 目录:例如你有一张图片想在文章中使用,可以将其保存路径如:public/images/blog/my-post/image.jpg 。
- 在 Markdown 中使用绝对路径引用:假设上面的图片,你可以在文章 Markdown 文件中这样插入:

- 路径以斜杠开头 /,表示从网站根目录查找,对应到 public 文件夹。
按照上述方式放置和引用图片,好处是在本地写作时编辑器预览能找到图片,在 Astro 构建时也会将 public 下的图片原封不动地打包进最终的 dist 目录 。因此,无论开发环境还是部署后,图片都能正常显示。
反之,不要把图片直接和 Markdown 文件混在一起(例如放在 src/content 内)。因为 Astro 不会处理 src/content 目录下的二进制文件,如果你这么做,结果很可能是本地编辑器能看到图片(因为引用的是本地路径),但实际构建出的站点并没有那些图片文件,用户在浏览器访问时就会出现 404 找不到图片 。简单打个比方:只有把物品放进行李箱(public 文件夹)里,搬家公司(Astro 打包过程)才会帮你运走;如果你把东西留在房间地板上(src/content),最后它们一定会被落下。
总之,记住一条原则:所有希望在网站上引用的静态资源(图片、视频等)都放进 public/ 里。这样可以避免很多路径问题。
📌 小经验:我在实践中就踩过一次图片路径的坑——部署后发现文章里的图片无法加载。最后才意识到是自己偷懒把图片放错了地方。按官方建议调整到 public 后,问题迎刃而解。
现在,博客内容和资源都准备就绪,接下来就是部署环节了。我们将使用 gh-pages 工具,将生成的静态站点发布到 GitHub Pages 上。
5. 使用 gh-pages 部署博客到 GitHub Pages
要将 Astro 生成的静态网站部署到 GitHub Pages,我们可以借助社区提供的 gh-pages 工具实现。一种常见的方法是:在本地构建出静态文件,然后利用 gh-pages 将 dist 目录推送到仓库的 gh-pages 分支上。GitHub Pages 可以将该分支的内容自动发布为网站。下面是具体步骤:
-
安装部署工具:在项目中安装 gh-pages 作为开发依赖。
npm install --save-dev gh-pages
该包提供了命令行工具,方便我们将静态文件发布到指定分支。
-
配置 astro.config.mjs:打开项目根目录下的 astro.config.mjs,设置站点的 base 路径。
如果你的 GitHub 仓库名是比如 virtual-velocity,那么需要添加如下配置:
import { defineConfig } from 'astro/config';
export default defineConfig({
base: '/virtual-velocity/', // 基础路径:替换为你的仓库名,加前后斜杠
});
上述配置中的 base 用来告诉 Astro,你的网站将部署在 GitHub Pages 的哪一级路径下 。例如仓库名是 virtual-velocity,GitHub Pages 部署后访问路径会是 https://用户名.github.io/virtual-velocity/,所以这里 base 要设为 /virtual-velocity/。切记,这个路径一定要与你的仓库名称一致,否则部署后页面的静态资源(CSS、JS、图片)路径会对不上,导致网站样式错乱或资源404。我第一次部署时就忘了设置 base,结果网页打开后一片混乱,定位到问题后赶紧补上这个配置,重新部署才恢复正常。就像填写邮寄地址时别忘了写单元号一样,base 相当于告诉 Astro “网站会挂在仓库名对应的子目录下”,这样生成的链接才会精确无误。
如果你的博客仓库就是像 username.github.io 这种用户主页仓库(即整站部署在根域名下),那实际上不需要设置 base(或者设成 / 即默认值)。这里主要针对项目仓库的情况需指定 base。
- 添加部署脚本:在项目的 package.json 文件中加入一条方便的部署脚本。在 “scripts” 部分添加:
// package.json 部分内容
"scripts": {
"deploy": "astro build && gh-pages -d dist --branch gh-pages" // 构建并推送到gh-pages分支
}
这行脚本包含两个命令:先运行 astro build 构建出静态文件到 dist 文件夹,然后调用 gh-pages -d dist —branch gh-pages 将 dist 目录部署到远程仓库的 gh-pages 分支上 。你也可以把这两步拆开执行,但为了方便,一键脚本是更好的选择。
- 执行部署命令:在项目根目录,运行下面的命令开始部署:
npm run deploy
脚本会进行静态构建并将结果推送到仓库。如果是你首次部署,该命令可能会要求输入 GitHub 凭证用于认证(因为要推送到远程仓库)。由于 GitHub 已不支持使用账号密码直接推送代码,你需要使用 Token 或 SSH 来认证(这一点我们稍后详述)。如果一切顺利,命令执行完毕后,GitHub 仓库应该已经更新了 gh-pages 分支。
- 验证 GitHub Pages 部署:部署完成后,登录 GitHub 打开你的仓库,进入 Settings → Pages 查看 GitHub Pages 的配置。通常,当仓库存在一个 gh-pages 分支时,GitHub 会默认使用它作为 Pages 源。如果没有生效,你可以在 Pages 设置中手动将 Source 设为 gh-pages 分支。稍等几十秒让 GitHub 完成发布,然后通过浏览器访问你的博客地址。对于项目仓库,网址一般是:
https://<你的 GitHub 用户名>.github.io/<你的仓库名>/
(将其中的用户名和仓库名替换为你自己的。)例如,前面的仓库名例子,访问 https://yourname.github.io/virtual-velocity/ 就应该能看到部署后的博客主页了。
注意:如果 gh-pages 部署过程中提示错误 “分支已存在” 或 push 被拒绝,可以在脚本命令里添加 —force 参数强制覆盖远程分支。例如:gh-pages -d dist —branch gh-pages —force。下一节的问答中我们也涵盖了这个问题。
通过以上步骤,我们就把 Astro 生成的静态博客成功发布到了 GitHub Pages 上。整个流程并不复杂,一旦配置完成,以后更新内容只需一条命令就能部署,非常高效。不过在实战过程中,可能会遇到一些坑和问题,我们接下来总结一下常见的问题及解决方案。
6. 遇到的问题及解决方案汇总
在我搭建部署博客的过程中,曾遇到过一些小问题。下面整理了几项常见的问题和对应的解决方法,方便你对照排查:
- sharp 模块缺失:首次运行项目如果出现类似 “Error: Cannot find module ‘sharp’” 的错误,这是因为 Astro 的图像优化功能默认使用了 sharp 库,但某些模板可能未自动安装它。解决办法很简单:执行 npm install sharp 安装该依赖。或者你也可以安装 Astro 提供的 Squoosh 图像处理集成来替代本地 sharp(Squoosh 基于 WebAssembly,无需本地依赖)。
- 文章中的图片在编辑器预览可见但部署后浏览器 404:这通常是因为图片文件放错了位置。请确保将图片存放在 /public/ 目录,并使用以 / 开头的绝对路径引用(详见前文第4节)。只有放在 public 下的资源才会被正确打包到网站中,否则构建后找不到文件,浏览器自然报 404。
- 构建输出路径为 /about/index.html 而不是 /about.html:这是 Astro 的默认行为。如果你希望直接生成根目录下的 about.html 文件,可以将页面文件命名为 about.html.astro(参考前文 3.2 节),这样构建结果就是单个 HTML 文件而非子目录。
- 使用 gh-pages 部署时报错“remote branch already exists”:这表示目标分支上已经有内容,gh-pages 默认不覆盖已有分支。解决方法是在部署命令后加上 —force 参数强制推送覆盖。例如:gh-pages -d dist —branch gh-pages —force。但请注意,强制推送可能覆盖之前在该分支上的改动,谨慎使用。
- 推送代码到 GitHub 遇到 SSH 连接错误 (端口 22 被阻挡):有些网络环境(例如公司内网、校园网)会阻断 SSH 所使用的 22 端口。如果你的本地仓库使用 SSH 链接远程仓库,部署时可能连不上 GitHub。解决方案是改用 HTTPS 协议的远程地址:运行 git remote set-url origin https://github.com/yourname/yourrepo.git 将远程地址切换为 HTTPS。HTTPS 通常走443端口,普遍不会被封锁。
- GitHub 不接受账户密码推送:如今 GitHub 已经禁用了通过账号密码进行 Git 操作的方式。如果部署时终端要求输入用户名/密码,你即便输入GitHub账户密码也会失败。正确做法是使用 Personal Access Token 作为密码,或者事先配置好 SSH Key 来认证身份(具体见下一节 GitHub Token 鉴权配置)。总之,确保你的本地 Git 能够成功推送到 GitHub——这取决于你是否配置了 Token 或 SSH。
以上问题中,特别是最后关于 GitHub 认证的部分非常关键。如果没有正确配置令牌(Token)或 SSH,你的代码是推不上去的。下面我们来详细说明一下 GitHub 的鉴权设置。
7. GitHub Token 鉴权配置(重要)
自 2021 年末起,GitHub 已禁用使用帐户密码进行 Git 推送。也就是说,当你执行 git push 或通过类似 gh-pages 工具推送内容到 GitHub 时,不能再用用户名+密码的方式登录认证,否则会失败。取而代之,你有两个选择:
-
使用 Personal Access Token 进行认证:Personal Access Token(简称 PAT)是由GitHub提供的一串令牌,可以代替密码进行命令行操作。你需要先登录 GitHub,在账户设置的 Developer Settings 中生成一个 PAT(选择 classic 类型,勾选.repo 权限) 。生成后复制保存这串 Token。在推送代码时,当终端要求输入密码时,就粘贴这个 Token。注意,Token 只会显示一次,务必妥善保存;而且出于安全考虑,不要把 Token 明文写在仓库代码里或分享给他人。如果不小心泄露了 Token,可以在 GitHub上立即作废它。以后每次推送都使用 Token 替代密码即可。
💡 提示:Token 就像 GitHub 发给你的“通行证”,权限范围由你创建时选择。一般用于仓库读写的 Token 需要勾选 repo 权限。生成 Token 的界面可以通过 GitHub 设置页面 打开。
-
改用 SSH Key 进行认证:SSH 是另一种常用且安全的 Git 认证方式。如果你已经在本地生成了 SSH 公私钥对,并将公钥添加到 GitHub 帐号,那么可以使用 SSH 来免密推送代码。方法是将本地仓库的远程地址改为 SSH 格式。例如:
git remote set-url origin [email protected]:yourname/yourrepo.git
把上面的 yourname/yourrepo 替换为你的 GitHub 用户名和仓库名。执行该命令后,本地仓库的 origin 地址就从 https:// 改为了 [email protected](SSH形式)。之后在有网络通畅的情况下(确保22端口不被拦截,或你在 .ssh/config 中配置了替代端口),Git 推送就会通过 SSH 完成,不再需要输入密码或 Token。使用 SSH 的前提是你已经在本机生成过 SSH Key并将公钥添加到 GitHub,如果还没有,可以参考 GitHub 官方文档生成并添加 SSH Key。
对于大多数个人开发者来说,使用 Token 认证是比较直观的方式:只需生成一次,然后每次部署用它即可。而 SSH 适合经常使用 Git 的开发者配置,一劳永逸免密码。如果你按照本文流程走下来,建议至少提前准备好上述两种方案之一,否则在部署时可能会因为没有权限推送而卡住。
总结
通过 Astro + GitHub Pages 的组合,我们可以相当迅速地搭建起一个现代化、极简且易维护的个人博客。从创建项目、个性化内容,到配置部署脚本、解决常见问题,每一步都相对清晰。一旦把这些配置琐事都处理好,今后写完文章执行一条 npm run deploy 命令,就能把最新博客内容发布上线,真正达到所写即所得的效果 。
回顾整个流程,你会发现搭建一个博客并没有想象中那么难。借助 Astro 强大的静态生成能力和 GitHub Pages 免费的托管服务,个人博客的搭建成本几乎为零,你要做的就是专心写作和分享。希望这篇指南能够帮助你避开一些坑,顺利拥有自己的线上博客主页!🙂
🌐 示例地址(部署完成后访问):https://yourname.github.io/your-repo/ (将 yourname 和 your-repo 替换为你的 GitHub 用户名和仓库名)
🔧 配置自定义域名(如 blog.geyuxu.com)
以上步骤完成后,你的博客已经可以通过 GitHub 提供的默认域名访问(例如 https://yourname.github.io/yourrepo/)。但是很多人希望使用一个自己的域名,让博客看起来更加专业,比如将博客绑定到 blog.geyuxu.com 这样的个性域名。下面我们以此为例,介绍如何将 GitHub Pages 部署的博客绑定自定义域名:
-
在 GitHub 仓库中设置自定义域名:
首先,在 GitHub 上打开你的博客仓库的设置页面。依次点击 Settings → Pages,找到 “Custom domain” 设置项。在输入框中填入你准备绑定的域名,例如:blog.geyuxu.com。填写后,点击保存(Save)。建议勾选下面的 “Enforce HTTPS” 选项,这样当别人通过 http 访问时会自动跳转到安全的 https 上。
-
在项目的 public/ 目录中添加 CNAME 文件:
在本地项目的 public/ 文件夹下,新建一个名为 CNAME 的文件(没有扩展名的纯文本文件)。文件内容仅需一行,写上你的自定义域名,例如:
blog.geyuxu.com
保存该文件并确保提交到仓库(也就是包含在 Astro 构建的 dist 中)。CNAME 文件的作用是告诉 GitHub Pages 你的站点所对应的域名。
⚠️ 注意:CNAME 文件里不要包含 http:// 或 https:// 前缀,只写域名本身即可;并且确保文件中没有多余的空行或空格,否则可能识别出错。
-
配置域名 DNS 解析(在你的域名服务商后台操作):
登陆你购买域名的服务商控制台,在 DNS 设置中添加一条记录,将你的自定义域名指向 GitHub Pages。具体配置如下:
- 记录类型:CNAME
- 主机记录:填写子域名前缀,例如使用 blog.geyuxu.com 就填 blog(如果想直接绑定顶级域名如 geyuxu.com,主机记录留空或填 @,但顶级域名绑定还需添加 A 记录,稍后提及)
- 指向(值):填写 yourname.github.io,将其替换为你的 GitHub 用户名 后加 .github.io。这表示将该子域指向你的用户页面。
- TTL:默认值或自动。
例如,对于域名 blog.geyuxu.com,GitHub 用户名为 geyuxu 的情况,这条 CNAME 记录应是:主机记录 blog,值指向 geyuxu.github.io。配置保存生效后,blog.geyuxu.com 就会解析到 GitHub。
- 测试并访问:
DNS 修改通常需要一些时间生效(通常几分钟到几小时不等,取决于DNS服务商)。稍等片刻后,在浏览器中访问你的自定义域名,例如 https://blog.geyuxu.com,此时应该能够看到你的博客页面正常显示了。如果没有生效,耐心等待并检查上述步骤是否正确。
小贴士:
- 每次使用 npm run deploy 部署时,gh-pages 工具都会自动包含并上传 public/CNAME 文件,所以在第一次设置好后,后续无需每次手动去 GitHub 设置页面修改域名。只要 CNAME 文件在,GitHub Pages 就会一直将你的站点绑定到指定域名。
- 如果你绑定的是顶级域名(例如直接使用 geyuxu.com 而不是子域名),除了添加 CNAME 记录外,还需要在 DNS 中添加 A 记录,将裸域指向 GitHub Pages 的服务器 IP 地址(GitHub 提供了一组固定 IP)。具体的 IP 列表和说明可以参考 GitHub 官方文档。
脱敏说明:本文所有出现的表名、字段名、接口地址、变量名、IP地址及示例数据等均非真实,仅用于阐述技术思路与实现步骤,示例代码亦非公司真实代码。示例方案亦非公司真实完整方案,仅为本人记忆总结,用于技术学习探讨。
• 文中所示任何标识符并不对应实际生产环境中的名称或编号。
• 示例 SQL、脚本、代码及数据等均为演示用途,不含真实业务数据,也不具备直接运行或复现的完整上下文。
• 读者若需在实际项目中参考本文方案,请结合自身业务场景及数据安全规范,使用符合内部命名和权限控制的配置。Data Desensitization Notice: All table names, field names, API endpoints, variable names, IP addresses, and sample data appearing in this article are fictitious and intended solely to illustrate technical concepts and implementation steps. The sample code is not actual company code. The proposed solutions are not complete or actual company solutions but are summarized from the author's memory for technical learning and discussion.
• Any identifiers shown in the text do not correspond to names or numbers in any actual production environment.
• Sample SQL, scripts, code, and data are for demonstration purposes only, do not contain real business data, and lack the full context required for direct execution or reproduction.
• Readers who wish to reference the solutions in this article for actual projects should adapt them to their own business scenarios and data security standards, using configurations that comply with internal naming and access control policies.版权声明:本文版权归原作者所有,未经作者事先书面许可,任何单位或个人不得以任何方式复制、转载、摘编或用于商业用途。
• 若需非商业性引用或转载本文内容,请务必注明出处并保持内容完整。
• 对因商业使用、篡改或不当引用本文内容所产生的法律纠纷,作者保留追究法律责任的权利。Copyright Notice: The copyright of this article belongs to the original author. Without prior written permission from the author, no entity or individual may copy, reproduce, excerpt, or use it for commercial purposes in any way.
• For non-commercial citation or reproduction of this content, attribution must be given, and the integrity of the content must be maintained.
• The author reserves the right to pursue legal action against any legal disputes arising from the commercial use, alteration, or improper citation of this article's content.Copyright © 1989–Present Ge Yuxu. All Rights Reserved.