Hexo 是一个基于 Node.js 的静态博客框架,2012 年发布,中文社区里普及度最高的博客工具之一。它把 source/_posts/ 里的 Markdown 文章、EJS / Pug 模板和 _config.yml 配置,生成成 public/ 下的纯静态站点。
核心特点与优势:
上手路径固定:hexo init 一条命令生成完整项目,hexo new 建文章,hexo server 本地预览,hexo generate 出产物,四步就能跑通。
中文资料最多:遇到的主题、插件、报错,基本都能搜到中文答案,这是它相对 Hugo、Astro 最实际的优势。
主题生态丰富:NexT、Butterfly、Fluid、Volantis 等主题功能齐全,分类、标签、归档、搜索、评论基本都是配置项。
插件体系成熟:RSS、sitemap、搜索索引、部署、图片处理都有现成插件,npm install 后改配置即可。
一键部署:hexo-deployer-git 可以把 public/ 直接推到 GitHub Pages 或任意 Git 仓库,不用自己写同步脚本。
模板灵活:页面和布局用 EJS,需要深度定制时比 Hugo 的 Go 模板更容易找到参考。
需要提前知道的几点:
和 Hugo 的取舍:
| 维度 | Hexo | Hugo |
|---|---|---|
| 运行时 | Node.js | 单二进制 |
| 构建速度 | 秒级,文章多时变慢 | 毫秒级到亚秒级 |
| 中文主题 / 教程 | 最多 | 多 |
| 定制方式 | EJS / Pug + 插件 | Go 模板 + shortcode |
| 依赖管理 | npm,存在插件老化风险 | 无依赖,升级简单 |
| 部署 | 静态产物 + 一键 git 部署 | 静态产物 |
Hexo 的部署链路:hexo generate 生成 public/,把这个目录交给 Web 服务器。和 Hugo 一样是纯静态,区别是构建需要 Node 环境。
| 维度 | 服务器 + 域名 | GitHub Pages |
|---|---|---|
| 费用 | 服务器 + 域名 | 公开仓库免费 |
| 国内访问 | 备案后稳定 | *.github.io 不稳定 |
| 发布方式 | 本地构建后 rsync | push 后 Actions 自动构建 |
| 适合场景 | 正式博客、要国内速度 | 零成本起步 |
node -v 确认。bashnpm install -g hexo-cli
hexo init myblog
cd myblog
npm install # init 通常已自动执行,报错时手动补一次
目录结构:
myblog/ ├── _config.yml # 站点配置(主题、URL、插件都在这) ├── package.json ├── scaffolds/ # 文章模板 ├── source/ │ ├── _posts/ # 文章,一篇一个 .md │ └── about/ # 独立页面 ├── themes/ │ └── next/ # 主题 └── public/ # 构建产物,不提交 Git
_config.yml 里先改这几项:
yamltitle: 我的博客
subtitle: ''
description: '记录折腾与踩坑'
author: 你的名字
language: zh-CN
timezone: 'Asia/Shanghai'
url: https://example.com
root: /
permalink: posts/:title/
theme: next
url 不带末尾斜杠;root 在站点根目录时是 /,部署到子路径时写 /仓库名/。permalink 里的 :title 取的是文件名,不是 front matter 的标题,所以文件名用英文 slug。timezone 必须写,否则文章日期按服务器时区解析,归档会错一天。用 submodule 安装,方便 CI 拉取和后续升级:
bashgit init git submodule add https://github.com/next-theme/hexo-theme-next themes/next
根目录新建 _config.next.yml,把要覆盖的配置从 themes/next/_config.yml 复制过来改,升级主题时不会被冲掉。常用几项:
yamlscheme: Gemini
menu:
home: / || fa fa-home
archives: /archives/ || fa fa-archive
categories: /categories/ || fa fa-th-list
tags: /tags/ || fa fa-tags
about: /about/ || fa fa-user
social:
GitHub: https://github.com/your-name || fab fa-github
local_search:
enable: true
bashhexo new post "hello-world"
hexo server # http://localhost:4000
生成的 source/_posts/hello-world.md:
markdown---
title: 你好,世界
date: 2026-10-25 10:00:00
tags: [随笔]
categories: [生活]
---
正文从这里开始。
hello-world 决定 URL(/posts/hello-world/),front matter 的 title 决定显示标题。hexo new post "中文标题" 也会生成文件,但文件名可能带中文或转义,建议先起英文名再改 title。hexo server -p 5000 可换端口,hexo server -d 带草稿预览(草稿是 source/_drafts/)。bashhexo clean && hexo generate
public/ 就是最终产物。hexo clean 会清掉缓存和旧产物,改主题或配置后必须执行,否则会看到旧样式。
bashssh ubuntu@1.2.3.4
sudo apt update && sudo apt install -y nginx
sudo mkdir -p /var/www/myblog
sudo chown -R $USER:$USER /var/www/myblog
sudo ufw allow 'Nginx Full'
/etc/nginx/sites-available/myblog.conf:
nginxserver { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/myblog; index index.html; location / { try_files $uri $uri/ =404; } location ~* \.html$ { add_header Cache-Control "no-cache"; } location ~* \.(css|js|woff2?|png|jpe?g|webp|svg|ico)$ { expires 30d; add_header Cache-Control "public, max-age=2592000"; } error_page 404 /404.html; gzip on; gzip_types text/css application/javascript application/json image/svg+xml; gzip_min_length 1024; }
bashsudo ln -s /etc/nginx/sites-available/myblog.conf /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
bashsudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.com sudo certbot renew --dry-run
服务器 IP 假设 1.2.3.4:
| DNS 服务商 | 配置入口 | 记录 | 注意点 |
|---|---|---|---|
| Cloudflare | NS 改到 Cloudflare → DNS → Records | A @ / www → 1.2.3.4 | 橙云代理时 SSL/TLS 用 Full (strict);签证书先灰云 |
| 阿里云 / 腾讯云 | 云解析 DNS → 解析设置 | A @ / www → 1.2.3.4,TTL 600 | 国内服务器必须先备案 |
| DNSPod | 域名解析 → 添加记录 | 同上,TTL 600 | 改完等 TTL 过期再验证 |
bashhexo clean && hexo generate rsync -avz --delete public/ deploy@1.2.3.4:/var/www/myblog/
Windows Git Bash 无 rsync 时 scoop install rsync,或用 tar 管道:
bashtar -czf - -C public . | ssh deploy@1.2.3.4 'find /var/www/myblog -mindepth 1 -delete && tar -xzf - -C /var/www/myblog'
url / root 与最终访问路径一致hexo clean && hexo generate 无报错,public/index.html 存在public/ 在 .gitignore 里.gitmodules 已入库certbot renew --dry-run 通过--delete,删文章不残留<用户名>.github.io,_config.yml 用 url: https://<用户名>.github.io、root: /。url: https://<用户名>.github.io/<仓库名>、root: /<仓库名>/。root 写错的表现是:首页能开,CSS、JS 全 404。推代码前确认 .gitignore 有 public/、node_modules/、.deploy_git/。
仓库 Settings → Pages → Source 选 GitHub Actions。
.github/workflows/pages.yml(Hexo 官方文档模板,Node 版本改成你本地的大版本):
yamlname: Pages
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
submodules: recursive
- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Cache NPM dependencies
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.OS }}-npm-cache
restore-keys: |
${{ runner.OS }}-npm-cache
- name: Install Dependencies
run: npm install
- name: Build
run: npm run build
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./public
deploy:
needs: build
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
npm run build 对应 package.json 里的 hexo generate,hexo init 默认已经写好。
blog.example.com,保存后仓库里会生成 CNAME。source/CNAME(Hexo 会复制到 public/),这样重新部署不会丢。CNAME blog → <用户名>.github.io;根域名用四条 A 记录。_config.yml 的 url 改成新域名,否则 RSS、sitemap 里还是旧地址。不想用 Actions 时:
bashnpm install hexo-deployer-git --save
_config.yml:
yamldeploy:
type: git
repo: https://github.com/your-name/your-name.github.io.git
branch: gh-pages
# 默认 true,会过滤掉 .nojekyll 这类隐藏文件
ignore_hidden: false
bashhexo clean && hexo deploy
这种方式把产物推到 gh-pages 分支,Pages 的 Source 要改成「Deploy from a branch → gh-pages」。GitHub Pages 默认跑 Jekyll,会忽略 _ 开头的文件和目录,所以要在 source/ 放一个空的 .nojekyll;而 hexo-deployer-git 的 ignore_hidden 默认为 true,不加 ignore_hidden: false 时 .nojekyll 推不上去。用 Actions 上传 artifact 时 Jekyll 不参与,这两步都可以省。
| 现象 | 原因 | 处理 |
|---|---|---|
| 首页开、样式全丢 | root 没配成 /仓库名/ | 改 _config.yml 的 root,重新生成 |
构建报 Cannot find module | 依赖没提交或 submodule 没拉 | 提交 package.json / package-lock.json;workflow 保留 submodules: recursive |
| 主题显示默认样式 | theme: 名字与 themes/ 目录名不一致 | 目录是 next 就写 theme: next |
| 改配置后线上没变 | 没 hexo clean | 本地清理后重新生成;CI 每次是全新环境,不受影响 |
| 文章 404 | permalink 里的 :title 与文件名不一致 | 直接看 public/ 里生成的目录结构 |
| 部署后中文乱码 | language 未配置 / 文件不是 UTF-8 | 配 zh-CN,编辑器保存为 UTF-8 |
| 现象 | 优先检查 |
|---|---|
| 本地 4000 端口打不开 | 端口占用;hexo server -p 5000 |
| 归档日期差一天 | timezone: 'Asia/Shanghai' 没配 |
| 标签 / 分类页 404 | 没建对应的 page,或 front matter 里字段为空 |
| 图片 404 | 图片放 source/images/,正文写 /images/x.jpg;Markdown 相对路径在 Hexo 里不可靠 |
| 搜索无结果 | 没装 hexo-generator-searchdb 或主题未启用 local_search |
| Node 升级后构建失败 | 插件不兼容,降回 Node 20 或替换插件 |
Hexo 的博客能力基本都在配置里,不用写代码。
bashhexo new page categories hexo new page tags
分别编辑 source/categories/index.md 和 source/tags/index.md,front matter 补 type:
yaml---
title: 分类
date: 2026-10-25 10:00:00
type: categories
---
否则页面是空的。文章里用 categories: [生活] 和 tags: [随笔, 工具] 归类,分类支持层级(categories: [技术, 前端])。
bashnpm install hexo-generator-feed hexo-generator-sitemap --save
_config.yml:
yamlfeed:
type: atom
path: atom.xml
limit: 20
sitemap:
path: sitemap.xml
生成后访问 /atom.xml、/sitemap.xml 验证;在 NexT 的 _config.next.yml 里可以加 RSS 入口。
NexT 支持多种评论组件,在 _config.next.yml 里二选一:
yamlcomments:
active: giscus
giscus:
repo: your-name/blog
repo_id: R_xxx
category: Announcements
category_id: DIC_xxx
mapping: pathname
repo_id / category_id 在 giscus.app 按仓库生成。国内访客多、又不想让他们登录 GitHub 时,可以换成 Twikoo 或 Valine 一类需要额外部署后端的方案。
_config.yml 里 description、keywords 写清楚,url 与实际域名一致。hexo-generator-sitemap 生成站点地图,提交到搜索平台。og:url 是不是真实域名。permalink: posts/:title/),不要依赖百分比编码路径。只改 _config.next.yml(根目录覆盖配置),不要直接改 themes/next/ 里的文件:
source/_data/styles.styl(或主题支持的 custom file),升级不丢;git diff 对比。Markdown 在 source/_posts/,写作 = 提交,发布 = 推送后构建。
改 source/_posts/*.md → commit → push → GitHub Actions:npm run build → public/ → Pages → 服务器路线:本地 hexo generate 后 rsync
Hexo 的 hexo new 依赖本地 Node 环境,手机上基本不会装。所以手机端写作的方式是手动新建 md 文件:
source/_posts/,新建文件 your-slug.md;markdown---
title: 文章标题
date: 2026-10-25 10:00:00
tags: [随笔]
categories: [生活]
---
这比 Hugo / Astro 更依赖格式正确性:Hexo 对 front matter 里的日期格式比较敏感,date 写成 2026-10-25 可以,写成斜杠或带时区字符串可能解析出意外结果,统一用 YYYY-MM-DD HH:mm:ss 最稳。
| 方案 | 平台 | 优点 | 代价 |
|---|---|---|---|
| GitHub 网页 | 全平台 | 零安装,新建 md 即发布 | 不能 hexo server 预览;front matter 要手写对 |
| Obsidian + Git | iOS / Android / 桌面 | 本地库、离线写 | 目录要指向 source/_posts,冲突手动合并 |
| IntelliJ / VS Code | 桌面 | 可本地预览、hexo new 正常用 | 需要 Node 环境 |
permalink: posts/:title/ 用的是文件名。YYYY-MM-DD HH:mm:ss,timezone: Asia/Shanghai 不要删。source/images/,正文用 /images/xxx.jpg 绝对路径;Markdown 相对路径在 Hexo 里不可靠。_config.yml 的改动只在桌面端做,并且单独提交,便于回滚。source/_drafts/,不会进入构建产物。deploy.sh(GitHub Pages 路线删掉 rsync 行;用的是 hexo-deployer-git 则换成 hexo deploy):
bash#!/usr/bin/env bash
set -euo pipefail
msg="${1:-post: 更新}"
git pull --rebase
npx hexo clean
npx hexo generate
git add -A
git commit -m "$msg" || echo "没有需要提交的改动"
git push
# 服务器路线
rsync -avz --delete public/ deploy@1.2.3.4:/var/www/myblog/
echo "已发布 $(date '+%F %T')"
日常流程:hexo new 建稿 → 写完 ./deploy.sh "post: 标题" → 手机打开站点确认。手机端改 md 直接 Commit,剩下的交给 Actions。
本文作者:Weee
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!