Hexo 博客从 GitHub Pages 迁移到你自己的服务器。

这篇文章记录我把 Hexo + GitHub Pages 迁移到自己的服务器上的一套可用方案。

最终思路很简单:

  1. 服务器上准备一个裸仓库 hexo.git
  2. 本地执行 hexo d 时,把生成好的静态文件推送到这个仓库
  3. 服务器通过 post-receive 钩子自动把最新内容检出到网站目录
  4. Nginx 读取网站目录,对外提供静态页面
  5. 域名、SSL、外网访问交给 Nginx Proxy Manager 处理

这样做的好处是:

  • 本地更新博客后,直接 hexo clean && hexo g && hexo d
  • 服务器不需要手动上传文件
  • 部署路径固定,后续维护比较省心

这里主要参考了这篇文章的思路:

https://www.cnblogs.com/cheyaoyao/p/17836522.html#33-git-%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE

一、服务器端准备 Git 自动部署

1. 安装 Git

apt update
apt install git -y

git --version

2. 创建 git 用户

adduser git

如果你后面要让 git 用户通过钩子把文件写入站点目录,通常会涉及 sudo 或 ACL 权限控制。

如果要给 git 用户 sudo 权限,可以这样做:

chmod 740 /etc/sudoers
vim /etc/sudoers

找到:

root ALL=(ALL) ALL

在下面添加:

git ALL=(ALL) ALL

保存后恢复权限:

chmod 400 /etc/sudoers
passwd git

3. 创建裸仓库

cd /home/git
git init --bare hexo.git

这里创建的是一个裸仓库,它只负责接收推送,不直接用来当网站目录。

二、配置 Git 钩子自动发布

编辑钩子文件:

sudo vim /home/git/hexo.git/hooks/post-receive

写入下面内容:

#!/bin/bash
{
echo "[$(date '+%F %T')] Deploy start"

# 清理旧文件
rm -rf /data/hexo/*

# 把最新内容检出到网站目录
git --work-tree=/data/hexo --git-dir=/home/git/hexo.git checkout -f HEAD

echo "[$(date '+%F %T')] Deploy end"
} >> /home/git/hexo.git/deploy.log 2>&1

然后给钩子执行权限:

chmod +x /home/git/hexo.git/hooks/post-receive

这个钩子的作用是:

  • 每次本地 git pushhexo.git
  • 服务器自动把最新文件强制检出到 /data/hexo
  • 最终 Nginx 直接读取 /data/hexo 提供静态站点

三、给网站目录配置权限

这一步非常重要,也是最容易踩坑的地方。

很多人以为只要:

chown -R www-data:www-data /data/hexo

就够了,但实际往往不够。因为这里涉及两个角色:

  • git:负责写入、更新博客静态文件
  • www-data:Nginx 默认运行用户,负责读取博客文件

也就是说,git 要能写,www-data 要能读,而且 Nginx 不只是要读文件本身,还必须能“进入路径上的每一级目录”。

1. 先创建网站目录

sudo mkdir -p /data/hexo

2. 先理解 Linux 目录权限

目录权限和文件权限不完全一样:

  • r:可以查看目录内容列表
  • w:可以在目录里创建、删除、重命名
  • x:可以进入目录

对于 Nginx 来说,最关键的是:

它必须对路径上的每一级目录都至少有 x 权限,否则即使文件本身可读,也会报 403 或 502。

例如你的博客目录是:

/data/hexo/index.html

那么 Nginx 至少要能穿过:

  • /
  • /data
  • /data/hexo

所以不能只盯着 /data/hexo,父目录权限也要检查。

3. 推荐做法:使用 ACL 同时给 gitwww-data 授权

先安装 ACL 工具:

apt install acl -y

然后给 gitwww-data 分别授权:

sudo setfacl -R -m u:git:rwx /data/hexo
sudo setfacl -R -m u:www-data:rx /data/hexo

sudo setfacl -d -m u:git:rwx /data/hexo
sudo setfacl -d -m u:www-data:rx /data/hexo

这几条命令的含义:

  • u:git:rwxgit 用户可读、可写、可进入目录
  • u:www-data:rxwww-data 用户可读、可进入目录,但不能写
  • -R:递归作用到当前已有文件和目录
  • -d:设置默认 ACL,后续新生成的文件和目录也自动继承

4. 父目录也要保证可以进入

如果 /data 本身权限太严,Nginx 依旧读不到 /data/hexo

所以建议检查:

namei -l /data/hexo/index.html

这个命令会把路径的每一级权限拆开显示,非常适合排查 “为什么文件明明在,但 Nginx 读不到” 这种问题。

如果发现 /data 没有给 www-data 执行权限,可以补:

sudo setfacl -m u:www-data:x /data

如果 /data 下还有更深层目录,也要同样检查。

5. 查看 ACL 是否生效

getfacl /data/hexo

你会看到类似结果:

# file: /data/hexo
# owner: root
# group: root
user::rwx
user:git:rwx
user:www-data:r-x
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:git:rwx
default:user:www-data:r-x
default:group::r-x
default:mask::rwx
default:other::r-x

如果这里只给了 git,没给 www-data,Nginx 就会打不开文件。

6. 如果你想用 chown 的简单方式

也可以这样做:

sudo chown -R git:www-data /data/hexo
sudo find /data/hexo -type d -exec chmod 775 {} \;
sudo find /data/hexo -type f -exec chmod 664 {} \;

这种方式思路是:

  • 所有者给 git
  • 所属组给 www-data
  • 目录可进入、可读
  • 文件可读

但这种方式不如 ACL 灵活,后面如果文件来源复杂,ACL 更稳一些。

四、配置本地 Hexo 部署

在本地博客目录安装 Git 部署插件:

npm install hexo-deployer-git --save

修改博客根目录下的 _config.yml

deploy:
type: git
repo: git@your_server_ip:/home/git/hexo.git
branch: master

同时确认站点地址也改成你自己的域名:

url: https://www.zxzlimit.xin
root: /
permalink: :year/:month/:day/:title/

以后本地更新博客,直接执行:

hexo clean && hexo g && hexo d

如果还没配置 SSH 免密,每次部署都要输密码。

五、配置 SSH 免密登录

可以直接用:

ssh-copy-id git@your_server_ip

或者自己手动生成公钥并追加到服务器的 ~/.ssh/authorized_keys

配置完成后,再执行 hexo d 就不会反复输入密码了。

六、配置 Nginx 读取静态站点

我这里的做法是:

  • Nginx 监听本地端口 8123
  • Nginx Proxy Manager 负责把域名和 SSL 代理到这个端口

先安装 Nginx,然后编辑:

sudo vim /etc/nginx/sites-available/default

写入:

server {
listen 8123;
listen [::]:8123;

root /data/hexo;
index index.html;

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

检查配置:

sudo nginx -t

重启:

sudo systemctl restart nginx

此时本机应该可以通过下面地址访问:

http://服务器IP:8123

如果访问失败,优先检查三类问题:

1. Nginx 配置语法是否正确

sudo nginx -t

2. Nginx 是否真的在监听 8123

ss -tlnp | grep 8123

3. 权限是否真的到位

重点检查:

namei -l /data/hexo/index.html
getfacl /data/hexo
sudo -u www-data test -r /data/hexo/index.html && echo ok

最后这条非常实用,它能直接模拟 www-data 是否真的能读到文件。

如果输出不是 ok,就说明还是权限问题。

七、查看部署日志

如果本地推送成功了,但页面没更新,就去服务器上看钩子日志:

cat /home/git/hexo.git/deploy.log

常见问题基本都能从这里看出来,比如:

  • git 用户没权限写 /data/hexo
  • 钩子文件没执行权限
  • 仓库路径写错
  • 检出目录不存在

八、最终流程总结

以后你的更新流程就是:

  1. 本地写文章
  2. 执行:
hexo clean && hexo g && hexo d
  1. 本地把静态文件推送到服务器裸仓库
  2. 服务器钩子自动检出到 /data/hexo
  3. Nginx 直接读取 /data/hexo
  4. NPM 负责域名和 SSL 对外访问

这套方案的核心优点是:

  • 不需要每次手工上传静态文件
  • 不依赖 GitHub Pages
  • 更新路径稳定
  • 本地和服务器职责清晰

如果后面访问还是有问题,优先排查的顺序建议固定为:

  1. hexo d 是否推送成功
  2. deploy.log 是否有报错
  3. /data/hexo 是否真的有最新文件
  4. www-data 是否真的能读取这些文件
  5. nginx -t 是否通过
  6. 8123 端口是否正常监听

这样排查会比一开始就怀疑 Nginx、怀疑 Hexo、怀疑域名快很多。