在 Ubuntu 服务器上搭建 Hexo 博客:从安装到域名访问

小镇炼丹师 Lv2

最近我在一台 Ubuntu 服务器上重新搭建了自己的 Hexo 博客。

整个网站使用 Hexo 生成静态页面,采用 Redefine 主题,通过 Nginx 提供访问。由于服务器没有公网 IPv4 和 IPv6,因此最终使用 Cloudflare Tunnel 将网站发布到互联网,并绑定自己的域名。

一、最终的网站架构

整个博客的访问流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
访客

https://www.lideguo.com

Cloudflare

Cloudflare Tunnel

http://127.0.0.1:80

Nginx

/mnt/data/www/blog

Hexo 的源文件放在:

1
/mnt/data/hexo-blog

生成的网站文件部署到:

1
/mnt/data/www/blog

这样可以将博客源文件与对外提供访问的静态文件分开管理。

二、准备运行环境

我的服务器系统是 Ubuntu 22.04。

首先更新软件包索引:

1
sudo apt update

如果更新过程中只有某个第三方软件源连接失败,而 Ubuntu 和 NodeSource 软件源可以正常使用,一般不会影响后续安装。

安装 Git、Nginx 和 rsync:

1
sudo apt install -y git nginx rsync

确认 Node.js 和 npm 已经安装:

1
2
node -v
npm -v

如果没有安装 Node.js,可以使用 NodeSource 软件源安装合适版本。

三、创建 Hexo 博客

进入数据盘:

1
cd /mnt/data

创建博客时,目标文件夹必须为空。因此不要先在目标文件夹中执行 npm install hexo,否则 hexo init 会提示:

1
target not empty

正确的初始化方式是:

1
2
3
npx hexo-cli init hexo-blog
cd /mnt/data/hexo-blog
npm install

也可以先全局安装 Hexo CLI:

1
2
3
4
sudo npm install -g hexo-cli
hexo init /mnt/data/hexo-blog
cd /mnt/data/hexo-blog
npm install

初始化完成后,目录结构大致如下:

1
2
3
4
5
6
hexo-blog/
├── _config.yml
├── package.json
├── scaffolds/
├── source/
└── themes/

四、安装 Redefine 主题

我使用的是 Redefine 主题。

进入博客目录:

1
cd /mnt/data/hexo-blog

安装主题:

1
npm install hexo-theme-redefine

修改 Hexo 主配置:

1
nano /mnt/data/hexo-blog/_config.yml

找到 theme,修改为:

1
theme: redefine

然后创建主题配置文件:

1
2
cp node_modules/hexo-theme-redefine/_config.yml \
/mnt/data/hexo-blog/_config.redefine.yml

以后修改主题时,主要编辑:

1
/mnt/data/hexo-blog/_config.redefine.yml

不建议直接修改 node_modules 里的主题文件,因为重新安装依赖后,这些修改可能会丢失。

五、配置博客基本信息

编辑 Hexo 主配置:

1
nano /mnt/data/hexo-blog/_config.yml

主要配置如下:

1
2
3
4
5
6
7
8
9
10
title: 小镇炼丹师
subtitle: 记录技术、生活与折腾
description: 一个记录学习与实践的个人博客
author: 小镇炼丹师
language: zh-CN
timezone: Asia/Shanghai

url: https://www.lideguo.com
root: /
permalink: :year/:month/:day/:title/

注意,YAML 配置文件中同一个字段不能出现两次。

例如,如果配置文件里已经有一个 url:,就不要在下面再次添加,否则 Hexo 会报错:

1
YAMLException: duplicated mapping key

YAML 还需要注意以下几点:

  • 使用空格缩进,不要使用 Tab。
  • 字段后的冒号必须是英文冒号。
  • 不要把 Markdown 的三个反引号复制进配置文件。
  • 建议使用 UTF-8 编码保存文件。

六、配置 Redefine 主题

打开主题配置文件:

1
nano /mnt/data/hexo-blog/_config.redefine.yml

修改主题颜色

我将博客的主色调改成了蓝色:

1
2
3
colors:
primary: "#2563EB"
secondary: "#60A5FA"

导航栏配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
navbar:
auto_hide: false

color:
left: "#1D4ED8"
right: "#38BDF8"
transparency: 35

width:
home: 1200px
pages: 1000px

links:
首页:
path: /
icon: fa-regular fa-house

动态:
path: /moments/
icon: fa-regular fa-images

友链:
path: /links/
icon: fa-solid fa-link

search:
enable: false
preload: true

修改首页背景

将背景图上传到图床后,在主题配置中填写图片地址:

1
2
3
4
5
6
home_banner:
enable: true
style: fixed
image:
light: https://img.lideguo.com/site/blue-background.jpg
dark: https://img.lideguo.com/site/blue-background.jpg

配置首页打字机文字

Redefine 可以让首页文字以打字机效果循环显示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
home_banner:
title: " "

subtitle:
text:
- "山高路远,慢慢折腾。"
- "保持好奇,保持热爱。"
- "纸上得来终觉浅,绝知此事要躬行。"
- "生活明朗,万物可爱。"

hitokoto:
enable: false

typing_speed: 100
backing_speed: 50
starting_delay: 300
backing_delay: 2000
loop: true
smart_backspace: true

text_style:
title_size: 0rem
subtitle_size: 2.5rem

这里的:

1
title: " "

引号中间有一个空格,不能直接写成空字符串。某些版本的 Redefine 在标题为空时可能访问旧配置字段并导致页面生成失败。

七、本地预览博客

在部署前,可以先启动 Hexo 测试服务器:

1
2
cd /mnt/data/hexo-blog
npx hexo server -i 0.0.0.0

看到下面的信息说明启动成功:

1
Hexo is running at http://localhost:4000/

在局域网内可以通过以下地址访问:

1
http://服务器局域网IP:4000

测试完成后按 Ctrl+C 停止服务。

Hexo 自带的服务器适合预览,不建议直接作为正式网站服务器,因此正式部署使用 Nginx。

八、生成静态网站

执行:

1
2
3
cd /mnt/data/hexo-blog
npx hexo clean
npx hexo generate

生成成功后,网站文件会出现在:

1
/mnt/data/hexo-blog/public

检查首页是否存在:

1
ls -l /mnt/data/hexo-blog/public/index.html

一定要确认 public/index.html 存在,再执行后面的同步操作。

九、把网站部署到 Nginx

创建网站目录:

1
2
3
4
sudo install -d -m 755 \
-o www-data \
-g www-data \
/mnt/data/www/blog

同步 Hexo 生成的静态文件:

1
2
3
4
sudo rsync -a --delete \
--chown=www-data:www-data \
/mnt/data/hexo-blog/public/ \
/mnt/data/www/blog/

需要特别注意:

1
/mnt/data/hexo-blog/public/

这里的 public 位于博客根目录中,而不是:

1
/mnt/data/hexo-blog/source/public/

如果在 source 目录中执行相对路径命令,很容易遇到:

1
change_dir ".../source/public" failed

所以我更喜欢在部署命令中使用绝对路径。

十、配置 Nginx

创建网站配置:

1
sudo nano /etc/nginx/sites-available/hexo-blog

写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server {
listen 80 default_server;
listen [::]:80 default_server;

server_name www.lideguo.com lideguo.com 192.168.8.184 _;

root /mnt/data/www/blog;
index index.html;

location / {
try_files $uri $uri/ /index.html;
}

location ~* \.(jpg|jpeg|png|gif|webp|svg|ico|woff|woff2|ttf)$ {
expires 30d;
add_header Cache-Control "public, max-age=2592000";
try_files $uri =404;
}

location ~* \.(css|js)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
try_files $uri =404;
}
}

启用配置:

1
2
sudo ln -s /etc/nginx/sites-available/hexo-blog \
/etc/nginx/sites-enabled/hexo-blog

如果链接已经存在,就不需要重复执行。

检查 Nginx 配置:

1
sudo nginx -t

重新加载:

1
sudo systemctl restart nginx

查看状态:

1
sudo systemctl status nginx --no-pager -l

在服务器本机测试:

1
curl -I http://127.0.0.1/

如果返回:

1
HTTP/1.1 200 OK

说明 Hexo 静态页面已经可以通过 Nginx 正常访问。

十一、解决 Nginx 返回 404

我在配置过程中遇到过浏览器访问服务器时显示:

1
2
404 Not Found
nginx

排查时可以执行:

1
2
sudo nginx -T 2>&1 | grep -nE \
"configuration file|listen|server_name|root|index"

确认 Nginx 实际加载了正确的网站配置。

然后检查端口:

1
sudo ss -ltnp '( sport = :80 )'

再分别测试:

1
2
curl -I http://127.0.0.1/
curl -I http://192.168.8.184/

如果这两个地址都返回 200 OK,说明 Nginx 和博客文件没有问题。

我当时还安装过宝塔面板,因此怀疑系统中存在两套 Nginx。不过最终通过检查监听端口和主进程,确认当前监听 80 端口的是 Ubuntu 自带的 Nginx:

1
ps -eo pid,ppid,user,args | grep '[n]ginx: master'

十二、没有公网 IP 怎么绑定域名

这台服务器没有公网 IPv4,也没有可用的公网 IPv6,因此不能直接将域名解析到服务器。

我最后选择了 Cloudflare Tunnel。

它的优点是:

  • 不要求服务器拥有公网 IP。
  • 不需要在路由器上做端口转发。
  • 不需要对公网开放 80 和 443 端口。
  • Cloudflare 可以自动提供 HTTPS。
  • 即使家庭宽带 IP 变化,也不影响访问。

域名接入 Cloudflare

虽然域名是在阿里云购买的,但可以把 DNS 托管到 Cloudflare。

添加域名后,Cloudflare 会提供两条 Nameserver。然后在阿里云域名控制台中,将原来的 DNS 服务器替换为 Cloudflare 提供的地址。

图床
图床

图床
图床

等待生效后,就可以在 Cloudflare 中管理域名解析。

创建 Tunnel

在 Cloudflare 联网菜单 中创建一个名为 blog-lmzy 的 Tunnel,然后按照页面提供的命令,在 Ubuntu 服务器中安装 cloudflared 服务。

图床
图床

Tunnel 正常连接后,状态会显示为“健康”。

图床
图床

添加公开路由

在 Tunnel 的“路由”页面添加“已发布的应用程序”,配置如下:

1
2
3
4
子域名:www
域名:lideguo.com
服务类型:HTTP
服务地址:127.0.0.1:80

图床
图床

最终访问地址就是:

1
https://www.lideguo.com

Cloudflare 会把请求转发到服务器本机的 Nginx,因此服务器即使没有公网 IP,也可以被互联网访问。

图床
图床

注意:Cloudflare Tunnel 的安装令牌相当于服务器凭据,不要把它发布到博客、聊天记录或代码仓库中。如果不小心泄露,应立即在 Cloudflare 后台轮换令牌。

十三、添加友链页面

创建友链页面:

1
2
mkdir -p /mnt/data/hexo-blog/source/links
nano /mnt/data/hexo-blog/source/links/index.md

内容如下:

1
2
3
4
5
6
---
title: 友情链接
date: 2026-08-04
template: links
comment: true
---

创建友链数据:

1
2
mkdir -p /mnt/data/hexo-blog/source/_data
nano /mnt/data/hexo-blog/source/_data/links.yml

示例:

1
2
3
4
5
6
7
- links_category: 朋友们
has_thumbnail: false
list:
- name: 小镇炼丹师
link: https://www.lideguo.com
description: 这是一段描述
avatar: https://img.lideguo.com/site/avatar-v1.jpg

友链页面地址为:

1
https://www.lideguo.com/links/

十四、添加朋友圈式动态页面

Redefine 主题支持类似“说说”的动态页面。

创建页面:

1
2
mkdir -p /mnt/data/hexo-blog/source/moments
nano /mnt/data/hexo-blog/source/moments/index.md

内容如下:

1
2
3
4
5
6
---
title: 动态
date: 2026-08-04
type: shuoshuo
comment: false
---

创建动态数据文件:

1
nano /mnt/data/hexo-blog/source/_data/essays.yml

示例:

1
2
3
4
5
6
7
8
9
- content: |
今天给博客增加了动态页面。

![博客背景](https://img.lideguo.com/site/blue-background.jpg)
date: "2026-08-04 18:30:00"

- content: |
博客终于搭建完成了,记录一下。
date: "2026-08-04 16:20:00"

新的动态放在文件最上面。

动态页面地址为:

1
https://www.lideguo.com/moments/

十五、发布新文章

创建文章:

1
2
cd /mnt/data/hexo-blog
npx hexo new post "我的第一篇文章"

文章位于:

1
/mnt/data/hexo-blog/source/_posts/

常见的文章头部格式如下:

1
2
3
4
5
6
7
8
9
---
title: 我的第一篇文章
date: 2026-08-04 20:00:00
categories:
- 学习记录
tags:
- Hexo
description: 文章摘要
---

正文中可以添加:

1
<!-- more -->

首页只会展示这个标记前面的摘要,能够避免手机首页显示过多正文。

十六、处理文章中的本地图片

在 Hexo 主配置中启用文章资源文件夹:

1
2
3
4
5
post_asset_folder: true

marked:
prependRoot: true
postAsset: true

例如文章为:

1
source/_posts/深度学习入门/2024-09-08.md

对应图片文件夹应该是:

1
source/_posts/深度学习入门/2024-09-08/

目录结构如下:

1
2
3
4
2024-09-08.md
2024-09-08/
├── image1.png
└── image2.jpg

Markdown 中引用图片:

1
![示例图片](image1.png)

不要写成:

1
![示例图片](./image1.png)

可以使用下面的命令批量删除某篇文章图片路径中的 ./

1
2
sed -i 's#](\./#](#g' \
/mnt/data/hexo-blog/source/_posts/文章路径.md

十七、每次更新博客的固定流程

以后新增或修改文章后,只需要执行:

1
2
3
4
5
6
cd /mnt/data/hexo-blog

npx hexo clean
npx hexo generate

test -f /mnt/data/hexo-blog/public/index.html

确认生成成功后,再部署:

1
2
3
4
sudo rsync -a --delete \
--chown=www-data:www-data \
/mnt/data/hexo-blog/public/ \
/mnt/data/www/blog/

完整流程可以概括为:

1
2
3
4
5
6
7
8
9
10
11
修改文章或配置

清理 Hexo 缓存

生成 public 静态页面

检查 public/index.html

同步到 Nginx 网站目录

刷新浏览器

普通文章更新不需要重启 Nginx,也不需要重启 Cloudflare Tunnel。

如果电脑仍然显示旧样式,可以使用:

1
Ctrl + Shift + R

强制刷新浏览器缓存。

十八、常见问题总结

Hexo 提示目标文件夹不为空

错误:

1
target not empty

原因是运行 hexo init 前,文件夹中已经存在 package.jsonnode_modules 等文件。

应当在空目录中初始化 Hexo。

public 文件夹不存在

错误:

1
change_dir ".../public" failed

通常是因为:

  • hexo generate 没有成功。
  • 当前路径不正确。
  • YAML 配置文件存在语法错误。
  • source/public 误认为了博客根目录中的 public

先运行:

1
2
3
cd /mnt/data/hexo-blog
npx hexo clean
npx hexo generate

只有生成成功后,才能执行 rsync。

YAML 配置报错

出现:

1
YAMLException

应重点检查报错行以及它前面的几行。

常见原因包括:

  • 重复配置 url:
  • 缩进错误。
  • 使用了 Tab。
  • 引号没有闭合。
  • 复制了 Markdown 代码块标记。
  • 注释前多了反斜杠。

手机显示新样式,电脑还是旧样式

这通常是浏览器缓存造成的。

可以尝试:

1
Ctrl + Shift + R

或者打开开发者工具,在 Network 中勾选 Disable cache 后刷新。

十九、写在最后

搭建 Hexo 本身并不复杂,真正花时间的部分通常是域名、网络、Nginx、主题配置和图片路径。

尤其是在服务器没有公网 IP 的情况下,Cloudflare Tunnel 提供了一条很方便的解决路线:服务器只需要能够正常访问互联网,就可以通过自己的域名对外提供网站。

现在整个博客已经具备了:

  • 自定义域名和 HTTPS
  • Redefine 主题
  • 蓝色背景与主题配色
  • 打字机首页文字
  • 文章分类与标签
  • 友情链接
  • 朋友圈式动态
  • 独立图床
  • 静态资源缓存

博客并不是搭建完成就结束了。接下来最重要的事情,还是慢慢写下值得记录的内容。

山高路远,慢慢折腾。

  • 标题: 在 Ubuntu 服务器上搭建 Hexo 博客:从安装到域名访问
  • 作者: 小镇炼丹师
  • 创建于 : 2026-08-04 20:00:00
  • 更新于 : 2026-08-04 20:00:00
  • 链接: https://www.lideguo.com/2026/08/04/博客搭建/在Ubuntu服务器上搭建Hexo博客/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论