经过一番折腾,我的 Hugo 博客成功部署到了腾讯云 EdgeOne Pages。相比 Vercel,EdgeOne Pages 提供了全球加速和更接近国内用户的访问体验。本文将详细记录部署过程中遇到的问题和解决方案。

1. 为什么选择 EdgeOne Pages?

EdgeOne Pages 是腾讯云推出的全栈开发部署平台,主要优势:

  • 全球加速:超过 3200+ 边缘节点,访问速度快
  • 免费额度:个人项目基本够用
  • 支持 Functions:类似 Vercel 的 Edge Middleware,可以实现密码保护等功能
  • 国内访问友好:相比 Vercel,国内访问更稳定

2. 创建项目和基本配置

2.1 创建项目

在网页端 EdgeOne 控制面板中选择右边的「创建项目」,而不是左边的「添加站点」。添加站点是为已有网站做加速,我们要部署还没有存在的网站,所以要选择右边创建项目,并选择“导入Git仓库”。

2.2 连接 GitHub 仓库

授权 GitHub 后选择你的 Hugo 博客仓库。

2.3 构建设置

配置项
框架预设 Other
根目录 ./
输出目录 public
构建命令 留空(由配置文件控制)
安装命令 留空(由配置文件控制)

3. 创建 edgeone.json 配置文件

截止到目前2026年3月20日,EdgeOne Pages 没有预装 Hugo,需要手动下载。在项目根目录创建 edgeone.json

1
2
3
4
5
6
{
  "name": "hcllmsx-blog",
  "installCommand": "curl -L -o hugo.tar.gz https://github.com/gohugoio/hugo/releases/download/v0.158.0/hugo_extended_0.158.0_linux-amd64.tar.gz && tar -xzf hugo.tar.gz && npm install",
  "buildCommand": "./hugo --gc --minify && npx pagefind --site public --output-path public/pagefind",
  "outputDirectory": "public"
}

配置说明

字段 说明
installCommand 下载 Hugo 并解压,同时安装 npm 依赖(Pagefind)
buildCommand Hugo 构建并生成 Pagefind 搜索索引
outputDirectory 输出目录,Hugo 默认是 public

注意事项

  1. Hugo 版本:将 v0.158.0 替换为你本地使用的版本
  2. Extended 版本:如果你的主题使用了 SCSS/Sass,需要下载 hugo_extended 版本
  3. 不要使用 chmod:EdgeOne 构建环境中 chmod 命令会报权限错误,但 tar 解压的文件默认有执行权限

4. 环境变量配置

在 EdgeOne Pages 控制台的「设置」→「环境变量」中添加:

变量名 变量值 说明
HUGO_BASEURL / 使用相对路径,部署后再改为实际域名
AUTH_DURATION 86400 密码保护有效期(秒),可选

5. Functions:实现密码保护页面

EdgeOne Pages 支持 Functions 功能,类似 Vercel 的 Middleware,可以实现密码保护页面。

5.1 目录结构

在项目根目录创建 functions 目录:

functions/
└── posts/
    └── [[path]].js

5.2 Functions 代码

functions/posts/[[path]].js 内容:

 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
62
63
64
65
66
67
68
69
70
71
72
73
74
/**
 * EdgeOne Pages 密码保护中间件
 * 匹配 /posts/* 下所有路径,根据配置判断是否需要密码保护
 */

// 密码映射配置 - 在此添加需要密码保护的页面
const PASSWORDS_MAP = {
  '/posts/protected/你的私密文章/': 'your-password',
};

// 认证有效期(秒),默认 24 小时
const AUTH_DURATION = 86400;

/**
 * 处理所有 /posts/* 请求
 */
export async function onRequest(context) {
  const { request, env } = context;
  const url = new URL(request.url);
  
  // 解码路径并统一格式(尾部加斜杠)
  const decodedPath = decodeURIComponent(url.pathname).toLowerCase();
  const path = decodedPath.endsWith('/') ? decodedPath : `${decodedPath}/`;
  
  // 检查当前路径是否需要密码保护
  const expectedPass = PASSWORDS_MAP[path];
  if (!expectedPass) {
    // 不需要保护,直接放行访问静态资源
    return fetch(request);
  }
  
  // 获取 Cookie
  const cookieHeader = request.headers.get('cookie') || '';
  
  // 生成路径标识符(用于 Cookie 名称)
  const pathToken = btoa(encodeURIComponent(path).replace(/%([0-9A-F]{2})/g, (_, p1) => String.fromCharCode('0x' + p1))).replace(/[=/+]/g, '').substring(0, 16);
  const authCookieName = `auth_${pathToken}`;
  
  // 检查是否已认证
  if (cookieHeader.includes(`${authCookieName}=true`)) {
    return fetch(request);
  }
  
  // 检查密码提交
  const tempPass = cookieHeader.split(';').find(c => c.trim().startsWith('check_pw='))?.split('=')[1];
  const decodedPass = tempPass ? decodeURIComponent(tempPass) : null;
  
  // 验证密码
  if (decodedPass && decodedPass === expectedPass) {
    const duration = env?.AUTH_DURATION || AUTH_DURATION;
    const response = await fetch(request);
    
    response.headers.append('Set-Cookie', `${authCookieName}=true; Path=/; Max-Age=${duration}; HttpOnly; SameSite=Lax`);
    response.headers.append('Set-Cookie', `check_pw=; Path=/; Max-Age=0`);
    
    return response;
  }
  
  // 未认证或密码错误,重定向到密码页面
  const passwordUrl = new URL('/password/', request.url);
  passwordUrl.searchParams.set('next', url.pathname);
  
  if (decodedPass && decodedPass !== expectedPass) {
    return new Response(null, {
      status: 302,
      headers: {
        'Location': passwordUrl.toString(),
        'Set-Cookie': 'pw_error=1; Path=/; Max-Age=30; SameSite=Lax'
      }
    });
  }
  
  return Response.redirect(passwordUrl.toString(), 302);
}

5.3 动态路由说明

EdgeOne Pages Functions 支持动态路由:

文件名 匹配范围
[id].js 单级路径,如 /posts/123
[[path]].js 多级路径,如 /posts/a/b/c

5.4 与 Vercel Middleware 共存

如果你同时部署到 Vercel 和 EdgeOne Pages:

hcllmsx-blog/
├── middleware.js           # Vercel 专用
├── functions/              # EdgeOne Pages 专用
│   └── posts/
│       └── [[path]].js
├── content/
└── ...

两个平台的配置互不干扰,各自识别自己的文件。

6. 踩坑记录

6.1 Hugo 未安装

问题hugo: command not found

解决:EdgeOne Pages 构建环境没有预装 Hugo,需要在 installCommand 中下载。

6.2 chmod 权限错误

问题/usr/bin/chmod: Permission denied

解决:去掉 chmod +x hugo,tar 解压后的文件已有执行权限,直接使用 ./hugo 即可。

6.3 sh 执行二进制文件

问题hugo: cannot execute binary file

解决:不能用 sh hugo 执行二进制文件,应直接使用 ./hugo

6.4 环境变量在 URL 中不生效

问题:使用 ${HUGO_VERSION} 环境变量时 URL 解析异常

解决:直接在 URL 中写死版本号,或确保环境变量格式正确。

7. 多平台部署策略

你可以将同一个 Hugo 博客部署到多个平台:

ihcll.cn          → 主平台(EdgeOne Pages)
vercel.ihcll.cn   → Vercel

项目文件结构

hcllmsx-blog/
├── edgeone.json            # EdgeOne Pages 配置
├── middleware.js           # Vercel Middleware
├── functions/              # EdgeOne Pages Functions
│   └── posts/
│       └── [[path]].js
├── package.json            # npm 依赖(Pagefind)
├── hugo.yaml               # Hugo 配置
├── content/
├── themes/
└── static/

package.json 示例

1
2
3
4
5
6
7
8
9
{
  "name": "hcllmsx-blog",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@vercel/edge": "latest",
    "pagefind": "^1.4.0"
  }
}

@vercel/edge 是 Vercel 专用依赖,在 EdgeOne Pages 中不会被使用,但也不会导致构建失败。

8. 总结

EdgeOne Pages 部署 Hugo 博客的关键点:

  1. 使用 edgeone.json 配置文件,在 installCommand 中下载 Hugo
  2. 不要使用 chmod,直接 ./hugo 执行
  3. Functions 功能可以实现密码保护等高级功能
  4. 多平台兼容,可以同时部署到 Vercel 和 EdgeOne Pages

希望这篇教程能帮助到你!如果有问题,欢迎在评论区交流。