Cloudflare Workers + R2部署Hugo增强解决方案
Cloudflare Workers + R2部署Hugo网站,突破Workers文件数上限增强解决方案
下面我将详细解释文件位置和扩展功能实现,并提供完整的配置示例:
文件位置说明
worker.js
和wrangler.toml
:- 放在 Hugo 项目根目录(与
config.toml
同级) - 不上传到 R2 存储桶
- 这些是 Worker 的配置文件,只在部署时使用
- 放在 Hugo 项目根目录(与
R2 存储桶内容:
- 只包含 Hugo 生成的
public
目录中的文件 - 通过命令行或脚本上传,无需手动操作
- 只包含 Hugo 生成的
your-hugo-project/
├── archetypes/
├── content/
├── layouts/
├── public/ # Hugo 生成的静态文件(上传到 R2)
├── static/
├── worker.js # Worker 脚本
├── wrangler.toml # 配置文件
└── config.toml # Hugo 配置
扩展版 wrangler.toml
(增强功能)
name = "hugo-site-worker"
main = "worker.js"
compatibility_date = "2024-06-25"
node_compat = true # 启用 Node.js 兼容模式
# R2 存储桶配置
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "hugo-site-assets" # 替换为你的桶名
# 环境变量(多环境支持)
[vars]
GA_ID = "UA-XXXXX-1" # Google Analytics ID
SITE_URL = "https://yourdomain.com"
# 部署环境配置(生产/预览)
[env.production]
name = "hugo-site-prod"
vars = { ENV = "production" }
[env.preview]
name = "hugo-site-preview"
vars = { ENV = "preview" }
# 自定义域绑定
routes = [
{ pattern = "yourdomain.com/*", zone_id = "your-zone-id" },
{ pattern = "www.yourdomain.com/*", zone_id = "your-zone-id" }
]
# 构建命令(自动构建时使用)
[build]
command = "npm run build"
upload_dir = "public"
扩展版 worker.js
(增强功能)
// ===== 配置区 =====
const CONFIG = {
// SEO 优化
trailingSlash: false, // 是否强制尾部斜杠
// 安全头
securityHeaders: {
"X-Content-Type-Options": "nosniff",
"X-Frame-Options": "DENY",
"X-XSS-Protection": "1; mode=block",
"Referrer-Policy": "strict-origin-when-cross-origin",
"Permissions-Policy": "geolocation=(), microphone=(), camera=()",
},
// 缓存策略
cacheRules: {
html: "public, max-age=600", // 10分钟
data: "public, max-age=3600", // 1小时
assets: "public, max-age=2592000", // 30天
},
// 重定向规则
redirects: {
"/old-page": "/new-page",
"/blog/2020/": "/archive/",
},
// 多语言支持
languages: ["en", "zh"],
defaultLanguage: "en",
};
// ===== 工具函数 =====
// 获取内容类型
const getContentType = (filename) => {
const extMap = {
html: "text/html; charset=utf-8",
css: "text/css",
js: "application/javascript",
json: "application/json",
png: "image/png",
jpg: "image/jpeg",
jpeg: "image/jpeg",
gif: "image/gif",
svg: "image/svg+xml",
ico: "image/x-icon",
txt: "text/plain",
webp: "image/webp",
webm: "video/webm",
mp4: "video/mp4",
pdf: "application/pdf",
woff: "font/woff",
woff2: "font/woff2",
xml: "application/xml",
};
const ext = filename.split(".").pop().toLowerCase();
return extMap[ext] || "application/octet-stream";
};
// 获取缓存策略
const getCacheControl = (filename) => {
if (filename.endsWith(".html")) return CONFIG.cacheRules.html;
if (filename.match(/\.(json|xml|rss)$/)) return CONFIG.cacheRules.data;
return CONFIG.cacheRules.assets;
};
// ===== 主处理器 =====
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
let pathname = url.pathname;
// 1. 处理重定向
if (CONFIG.redirects[pathname]) {
return Response.redirect(CONFIG.redirects[pathname], 301);
}
// 2. 处理语言重定向
const langMatch = pathname.match(/^\/(en|zh)(\/|$)/);
const acceptLanguage = request.headers.get("Accept-Language") || "";
if (!langMatch && CONFIG.languages.length > 1) {
const preferredLang = acceptLanguage.includes("zh") ? "zh" : CONFIG.defaultLanguage;
return Response.redirect(`/${preferredLang}${pathname}`, 302);
}
// 3. 处理尾部斜杠
if (CONFIG.trailingSlash && !pathname.endsWith("/") && !pathname.includes(".")) {
return Response.redirect(`${pathname}/`, 301);
}
// 4. 处理根路径
if (pathname === "/") pathname = "/index.html";
// 5. 生成对象名称
let objectName = pathname.replace(/^\//, "");
// 6. 尝试获取文件
try {
let object = await env.ASSETS.get(objectName);
// 7. 备用路径:index.html
if (!object && !objectName.endsWith("index.html")) {
const altPath = objectName.endsWith("/")
? `${objectName}index.html`
: `${objectName}/index.html`;
object = await env.ASSETS.get(altPath);
if (object) objectName = altPath;
}
// 8. 处理404
if (!object) {
const notFound = await env.ASSETS.get("404.html");
return notFound
? buildResponse(notFound, "404.html", 404)
: new Response("Not Found", { status: 404 });
}
// 9. 构建响应
return buildResponse(object, objectName);
} catch (err) {
// 10. 错误处理
return new Response("Internal Server Error", { status: 500 });
}
// 响应构建器
function buildResponse(object, filename, status = 200) {
const headers = new Headers();
// 内容类型
headers.set("Content-Type", getContentType(filename));
// 缓存控制
headers.set("Cache-Control", getCacheControl(filename));
// 安全头
Object.entries(CONFIG.securityHeaders).forEach(([key, value]) => {
headers.set(key, value);
});
// 添加环境变量
if (env.ENV === "preview") {
headers.set("X-Robots-Tag", "noindex");
}
// 添加Google Analytics(可选)
if (filename.endsWith(".html") && env.GA_ID) {
const gaScript = `
<script async src="https://www.googletagmanager.com/gtag/js?id=${env.GA_ID}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${env.GA_ID}');
</script>`;
let body = object.body;
// 如果是HTML,注入GA代码
if (getContentType(filename).startsWith("text/html")) {
body = body.replace(/<\/head>/i, `${gaScript}</head>`);
}
return new Response(body, { headers, status });
}
return new Response(object.body, { headers, status });
}
}
};
增强功能说明
多环境支持:
- 生产环境:
wrangler deploy --env production
- 预览环境:
wrangler deploy --env preview
- 自动添加
X-Robots-Tag: noindex
到预览环境
- 生产环境:
SEO 优化:
- 自动语言重定向(基于浏览器语言首选项)
- 尾部斜杠统一处理
- 301/302 重定向支持
- 自动注入 Google Analytics
安全增强:
- 完整的安全头设置(CSP, X-Frame-Options 等)
- 内容类型嗅探防护
- 权限策略控制
智能缓存:
- HTML:短缓存(10分钟)
- 数据文件:中等缓存(1小时)
- 静态资源:长缓存(30天)
错误处理:
- 自定义404页面
- 错误捕获和日志
- 备用路径自动查找
部署脚本 deploy.sh
#!/bin/bash
# 1. 清理并构建Hugo网站
hugo --minify --cleanDestinationDir
# 2. 上传到R2
echo "Uploading files to R2..."
wrangler r2 object put hugo-site-assets/ --directory ./public --recursive
# 3. 部署Worker(根据环境变量)
if [ "$ENV" = "production" ]; then
echo "Deploying to production..."
wrangler deploy --env production
else
echo "Deploying to preview..."
wrangler deploy --env preview
fi
# 4. 清除缓存(可选)
echo "Purging Cloudflare cache..."
curl -X POST "https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/purge_cache" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
echo "Deployment complete!"
高级功能扩展思路
自定义 Worker 逻辑(比如复杂的 URL 重写、A/B 测试、动态注入等)
- A/B 测试:
// worker.js 中添加
const AB_TESTS = {
"/": {
variants: [
{ weight: 50, path: "/index-v1.html" },
{ weight: 50, path: "/index-v2.html" }
]
}
};
// 在请求处理中
if (AB_TESTS[pathname]) {
const test = AB_TESTS[pathname];
const random = Math.random() * 100;
let cumulative = 0;
for (const variant of test.variants) {
cumulative += variant.weight;
if (random <= cumulative) {
objectName = variant.path.replace(/^\//, "");
break;
}
}
}
- API 代理:
// 添加API代理规则
if (pathname.startsWith("/api/")) {
const apiUrl = `https://your-backend.com${pathname.replace('/api', '')}`;
const apiResponse = await fetch(apiUrl, {
headers: request.headers,
method: request.method,
body: request.body
});
// 添加CORS头
const response = new Response(apiResponse.body, apiResponse);
response.headers.set("Access-Control-Allow-Origin", "*");
return response;
}
- 访客分析:
// 在响应后执行
ctx.waitUntil(logVisit(request));
async function logVisit(request) {
const url = new URL(request.url);
const data = {
timestamp: new Date().toISOString(),
path: url.pathname,
country: request.cf.country,
ua: request.headers.get("user-agent"),
ip: request.headers.get("cf-connecting-ip")
};
// 存储到R2(或Workers KV)
await env.ASSETS.put(`logs/${Date.now()}.json`, JSON.stringify(data));
}
部署流程总结
- 初始设置:
npm install -g wrangler
wrangler login
wrangler r2 bucket create hugo-site-assets
- 日常部署:
# 设置环境变量
export ENV=production
export CLOUDFLARE_API_TOKEN=your_token
# 执行部署
chmod +x deploy.sh
./deploy.sh
- 调试技巧:
# 本地测试
wrangler dev
# 查看日志
wrangler tail
这种配置提供了企业级的 Hugo 部署方案,解决了文件数量限制问题,同时增加了高级功能如多环境部署、A/B 测试、API 代理和访客分析,完全利用 Cloudflare 边缘网络的优势。