利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器
利用 GitHub Actions 自动部署 Hexo 到 GitHub Pages 并通过 WebHook 安全更新服务器
最近想恢复写博客,然后就在在折腾 Hexo,我一直希望能够实现自动化部署,实现我博客内容更新后自动构建自动部署到我的服务器中*。最初尝试过直接用 FTP 上传网站,但问题是FTP需要开放额外的端口,而且 FTP 本身也存在安全隐患。于是我决定尝试通过 GitHub Actions 打包 Hexo 站点,然后推送到 GitHub Pages,同时通过 WebHook 通知服务器下载最新压缩包更新网站目录。整个流程安全稳定,因此记录。
下面我就分享整个流程的实现方法,包括每个命令和配置的详解。
一、为什么不用 FTP
起初我考虑用 FTP 上传 Hexo 生成的 public 文件夹到服务器。理论上这是最简单的方式,但实践中有几个问题:
- 安全性问题
FTP 需要开放 21 端口,密码传输容易被抓包,而且需要开放额外的传输端口。即使使用 FTPS,也要额外配置证书。
- 维护复杂
每次部署都需要判断哪些文件被删除、哪些文件新增,FTP 本身不能轻松做到增量同步。
- 效率问题
上传大量静态文件容易耗时,如果网络不稳定,部署可能中断。
所以我决定走另一条路线:GitHub Actions + GitHub Pages + WebHook 下载压缩包到服务器。这样服务器只需要访问 GitHub 或加速下载地址,省去 FTP 的安全和端口问题。
我最开始还不知道FTP必须得开放额外的数据传输端口…

二、GitHub Actions 自动打包 Hexo 并发布 GitHub Pages
在仓库根目录下新建 .github/workflows/deploy.yml,此文件将被Github Action自动识别为任务:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| name: Deploy Hexo to GitHub Pages and FTP
on: workflow_dispatch: push: branches: - master
permissions: contents: write
jobs: build: runs-on: ubuntu-latest
steps: - name: Checkout source uses: actions/checkout@v4
- name: Setup Node.js uses: actions/setup-node@v4 with: node-version: 20
- name: Install dependencies run: | npm install -g hexo-cli npm install
- name: Build Hexo run: | hexo clean hexo generate
- name: Deploy to GitHub Pages env: TOKEN: ${{ secrets.HEXO_TOKEN }} run: | cd public git init git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add . git commit -m "Deploy from GitHub Actions" git branch -M gh-pages git push --force "https://${{ secrets.HEXO_TOKEN }}@github.com/xuanrandev/XuanRanDev.github.io.git" gh-pages
- name: Notify BaoTa WebHook run: | echo "Notify WebHook..." curl -X GET "${{ secrets.WEBHOOK_ADDRESS }}"
|
关键点解析
- actions/checkout@v4:检出仓库代码。
- actions/setup-node@v3:安装指定版本 Node.js。
- Build Hexo:生成静态站点到
public。
- git init + git add + git commit + git push:把生成的内容推送到
gh-pages 分支,实现 GitHub Pages 发布(记得修改XuanRanDev.github.io这个值为你想要部署的仓库名称)。
- curl 通知 WebHook:GitHub Actions 执行完成后调用服务器 WebHook。
通过这种方式,服务器不需要开放 FTP 端口,也不需要用户直接上传文件。
记得在Github仓库的设置中添加HEXO_TOKEN 和 WEBHOOK_ADDRESS 这两个值
三、服务器端部署脚本
在服务器上,我创建了一个专门的部署用户,因为直接通过root执行命令会有很大的安全风险:
1 2 3 4 5
| useradd -r -s /usr/sbin/nologin -d /home/hexodeploy hexodeploy mkdir -p /home/hexodeploy chown hexodeploy:hexodeploy /home/hexodeploy chmod 750 /home/hexodeploy
|
-r 表示系统账户,不会分配登录 shell。
-s /usr/sbin/nologin 禁止 SSH 或 su 登录。
/home/hexodeploy 是家目录,用于存放脚本和临时文件。
然后部署目录设置为该用户可写:
1 2
| chown -R hexodeploy:hexodeploy /www/wwwroot/blog.example.com chmod -R 755 /www/wwwroot/blog.example.com
|
部署脚本 deploy.sh
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #!/bin/bash set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR"
LOG_DATE=$(date '+%Y-%m-%d %H:%M:%S')
if [ ! -f .env ]; then echo "❌ [${LOG_DATE}] 错误: .env 文件不存在" exit 1 fi
source .env
TMP_DIR="$SCRIPT_DIR/tmp" ZIP_FILE="$TMP_DIR/site.zip"
echo "=========================================" echo "🚀 Hexo 自动部署启动" echo "执行用户: $(whoami)" echo "时间: ${LOG_DATE}" echo "下载地址: $ZIP_URL" echo "目标目录: $TARGET_DIR" echo "========================================="
mkdir -p "$TMP_DIR"
echo "⬇️ 下载站点压缩包..." if ! wget -q -O "$ZIP_FILE" "$ZIP_URL"; then echo "❌ 下载失败: $ZIP_URL" rm -rf "$TMP_DIR" exit 1 fi
echo "🧹 清理旧文件(保留 .user.ini)..." find "$TARGET_DIR" -mindepth 1 ! -name '.user.ini' -exec rm -rf {} + || true
echo "📦 解压新文件..." unzip -q "$ZIP_FILE" -d "$TMP_DIR"
EXTRACTED_DIR=$(find "$TMP_DIR" -mindepth 1 -maxdepth 1 -type d | head -n 1) if [ -z "$EXTRACTED_DIR" ]; then echo "❌ 解压失败:未找到内容" rm -rf "$TMP_DIR" exit 1 fi
echo "🚚 拷贝到目标目录..." cp -rf "$EXTRACTED_DIR"/* "$TARGET_DIR/"
rm -rf "$TMP_DIR"
if [ "$NGINX_RELOAD" = "true" ]; then systemctl reload nginx 2>/dev/null || echo "⚠️ Nginx 重载失败或未启用" fi
LOG_DATE_END=$(date '+%Y-%m-%d %H:%M:%S') echo "✅ [${LOG_DATE_END}] 部署完成!" echo "========================================="
|
.env文件
1 2 3 4 5 6 7 8 9
| # 网站部署目录 TARGET_DIR=/www/wwwroot/blog.example.com
# ZIP 下载地址 ZIP_URL=xxxxx
# 部署完成后是否自动重载 Nginx NGINX_RELOAD=false
|
说明
wget 下载:直接从 GitHub 或加速地址获取压缩包,无需 FTP。
我这里是把打包好的博客静态页面上传到了我的Github Page,然后Github支持直接下载代码压缩包,再套一个Github加速,就不用担心网络问题了
find … ! -name ‘.user.ini’:确保不会删除 PHP 配置文件。
cp -rf:覆盖旧站点文件。
注意,请将你的域名进行替换
调用方式
在WebHook 配置中,若收到请求直接执行以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #!/bin/bash
echo "接收到部署请求" ZIP_URL="${1:默认下载地址}" echo "下载地址: $ZIP_URL"
sudo -u hexodeploy bash /home/hexodeploy/deploy.sh
if [ $? -eq 0 ]; then echo "部署成功" exit 0 else echo "部署失败" exit 1 fi
|
sudo -u hexodeploy:切换到部署用户执行
WebHook 本身是支持传递参数过来,若传递的有参数则使用参数的下载地址,我这里直接使用默认了,记得替换上面的默认下载地址
四、效果
然后你就可以专注于博客的内容了,而无需关注打包和部署了,下面是我的运行效果

一旦你往仓库push新的代码了,就会触发自动构建和更新

然后博客内容就会进行更新
