本教程将指导您使用Cloudflare Workers和Workers KV搭建一个功能完整的短网址服务。该服务支持创建短链接、自动检测微信浏览器并显示提示,使用KV存储短链映射,免费额度足够个人使用。

功能特点

  • ✅ 支持创建短链接(如 /abc123 跳转到目标URL)
  • ✅ 自动检测是否在微信浏览器中打开
  • ✅ 如果在微信中打开,则显示提示:”请复制到浏览器打开”
  • ✅ 使用 Cloudflare KV 存储映射关系(免费额度足够个人使用)
  • ✅ 提供 Web 界面用于生成短链
  • ✅ 防止重复提交相同长链接(基于 SHA-1 哈希去重)
  • ✅ 阻止用户输入自身短链接(避免循环)
  • ✅ 防止短链重复生成(相同URL生成相同短码)
  • ✅ 友好的404错误页面
  • ✅ 移动端友好的响应式设计

准备工作

第一步:注册Cloudflare账号

访问 Cloudflare官网 注册账号。

第二步:创建Worker

  1. 登录Cloudflare控制台
  2. 进入Workers & Pages
  3. 点击”创建应用程序”
  4. 选择”创建Worker”
  5. 命名Worker(例如:url-shortener
  6. 点击”部署”

第三步:创建KV命名空间

  1. 在Worker详情页面,选择”设置”标签
  2. 点击”变量” → “KV命名空间绑定”
  3. 点击”添加绑定”
  4. 变量名称输入:URL_MAPPINGS
  5. 点击”创建新的KV命名空间”
  6. 命名空间名称输入:URL_MAPPINGS
  7. 点击”保存”

第四步:将KV绑定到Worker

  1. 在变量绑定部分,选择刚才创建的URL_MAPPINGS命名空间
  2. 点击”保存并部署”

核心代码实现

配置文件

1
2
const SHORT_CODE_LENGTH = 6; // 短码长度

工具函数

检测被禁用的UA

1
2
3
4
5
function isBannedUA(ua) {
ua = ua.toLowerCase();
return /micromessenger/i.test(ua) || /weibo/.test(ua) || /qq\//.test(ua);
}

SHA-1哈希生成

1
2
3
4
5
6
7
8
async function sha1(str) {
const encoder = new TextEncoder();
const data = encoder.encode(str);
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

生成短码

1
2
3
4
5
6
7
8
9
function generateShortCode() {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < SHORT_CODE_LENGTH; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}

页面模板

404错误页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>短链接不存在</title>
<!-- 样式和脚本 -->
</head>
<body>
<div class="container">
<div class="error-icon"></div>
<h1>短链接不存在</h1>
<p>您访问的短链接已失效或不存在</p>
<button class="btn" onclick="window.location.href='/'">返回主页</button>
</div>
</body>
</html>

微信拦截页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>提示</title>
<!-- 样式和脚本 -->
</head>
<body>
<div class="card">
<h2>⚠️无法在当前应用中打开</h2>
<p>点击右上角菜单</p>
<p>选择 <span class="highlight">"在浏览器中打开"</span></p>
</div>
</body>
</html>

主页(短链接生成器)

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
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>短链接生成器</title>
<!-- 样式和脚本 -->
</head>
<body>
<div class="container">
<h1>🔗 短链接生成器</h1>
<div class="card">
<form id="shorten-form">
<input type="url" id="url-input" placeholder="请输入完整网址(如 https://example.com)" required />
<button type="submit">生成短链接</button>
</form>
<div class="result"></div>
</div>
<!-- 友链部分 -->
<p class="links">友链:...</p>
</div>

<script>
// 客户端JavaScript代码
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('shorten-form');

// 事件委托处理复制按钮
document.addEventListener('click', (e) => {
if (e.target.classList.contains('copy-btn')) {
const shortUrl = e.target.closest('.result').querySelector('a').href;
copyLink(shortUrl);
}
});

// 表单提交处理
form.addEventListener('submit', async (e) => {
e.preventDefault();

// 客户端微信检测
if (/MicroMessenger/i.test(navigator.userAgent)) {
alert('请在浏览器中打开,不要在微信中使用');
return;
}

// 表单数据处理
// ...
});
});
</script>
</body>
</html>

请求处理器

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
async function handleRequest(request) {
const url = new URL(request.url);
const path = url.pathname;
const userAgent = request.headers.get('User-Agent') || '';
const inWechat = isBannedUA(userAgent);

// 1. 处理微信环境
if (inWechat && (path === '/' || path.startsWith('/'))) {
return new Response(WECHAT_BLOCK_PAGE, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}

// 2. 处理短链接跳转
const shortCodeMatch = path.match(/^\/([a-zA-Z0-9]{6})$/);
if (shortCodeMatch) {
const shortCode = shortCodeMatch[1];
const targetUrl = await LINKS.get(shortCode);
if (targetUrl) {
return Response.redirect(targetUrl, 302);
} else {
return new Response(NOT_FOUND_PAGE, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
}

// 3. 处理主页请求
if (request.method === 'GET' && path === '/') {
return new Response(homePage(), {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}

// 4. 处理短链接生成请求
if (request.method === 'POST' && path === '/') {
try {
// 表单数据处理
const formData = await request.formData();
let targetUrl = formData.get('url')?.trim();

// URL验证和规范化
if (!targetUrl) throw new Error('请输入网址');

if (!/^https?:\/\//i.test(targetUrl)) {
targetUrl = 'https://' + targetUrl;
}

const normalizedUrl = targetUrl.replace(/\/$/, '');

// 防止短链接循环
try {
const inputUrl = new URL(normalizedUrl);
const currentDomain = new URL(url.origin);

if (inputUrl.hostname === currentDomain.hostname &&
inputUrl.pathname.length === 7 &&
inputUrl.pathname[0] === '/' &&
/^[a-zA-Z0-9]{6}$/.test(inputUrl.pathname.substring(1))) {

return new Response('输入的URL是短链接,请输入长链接', {
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
} catch (e) {
throw new Error('网址格式无效');
}

// 检查URL是否已存在
const urlHash = (await sha1(normalizedUrl)).substring(0, 12);
const hashKey = `hash:${urlHash}`;
const existingCode = await LINKS.get(hashKey);

if (existingCode) {
return new Response(`${url.origin}/${existingCode}`, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}

// 生成新短码
const shortCode = generateShortCode();
const fullShortUrl = `${url.origin}/${shortCode}`;

// 存储到KV
await Promise.all([
LINKS.put(shortCode, normalizedUrl),
LINKS.put(hashKey, shortCode)
]);

return new Response(fullShortUrl, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});

} catch (e) {
console.error('短链生成失败:', e.message);
return new Response(`❌ ${e.message}`, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
status: 400
});
}
}

// 5. 处理404
if (path !== '/' && !shortCodeMatch) {
return new Response(NOT_FOUND_PAGE, {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
}
}

Worker入口点

1
2
3
4
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});

部署和使用

部署步骤

  1. 将完整代码复制到Worker编辑器中
  2. 点击”保存并部署”
  3. 访问您的Worker域名(如:your-worker.workers.dev

使用方法

  1. 访问短网址服务主页
  2. 在输入框中输入要缩短的完整URL
  3. 点击”生成短链接”按钮
  4. 复制生成的短链接(格式:your-domain.com/abc123
  5. 分享短链接,用户访问时会自动跳转到原始URL

功能说明

功能 实现方式
短链生成 POST / 提交 url 参数,返回 https://your-worker.example/abc123
跳转 访问 /abc123 → 302 重定向到原始 URL
微信拦截 检测 UA 中包含 MicroMessengerweiboQQ/,返回提示页
防重复 对规范化 URL 计算 SHA-1 哈希前 12 位,存为 hash:xxx,下次直接返回已有短码
防自环 禁止用户提交形如 https://your-worker.example/xxxxxx 的短链接
404 页面 所有无效路径均返回友好提示

安全与隐私说明

  • 本服务 不收集任何用户数据
  • 不使用 Cookie、LocalStorage(除临时复制外)
  • 所有逻辑运行在 Cloudflare 边缘节点
  • KV 存储仅包含:短码 → 长链接哈希 → 短码 映射

技术特性

1. 短码生成策略

  • 使用6位字符(大小写字母+数字)共62^6≈568亿种组合
  • 相同URL始终生成相同短码,避免重复存储

2. 性能优化

  • KV存储读写操作快速响应
  • 客户端和服务端双重微信检测
  • 响应式设计适配各种设备

3. 安全性

  • 输入URL验证和规范化
  • 防止短链接循环引用
  • 自动补全HTTPS协议

4. 用户体验

  • 简洁直观的操作界面
  • 一键复制短链接功能
  • 友好的错误提示页面
  • 微信环境下的明确指引

注意事项

  1. 免费额度限制:Cloudflare Workers免费套餐包括:

    • 每天10万次请求
    • KV存储:100,000次读取/天,1,000次写入/天
    • 存储空间:1GB
  2. 域名绑定:可以将自定义域名绑定到Worker,通过”触发器” → “自定义域”设置

  3. 监控和统计:可以在Cloudflare控制台查看Worker的请求统计和错误日志

  4. 备份建议:定期导出KV中的数据作为备份

故障排除

常见问题

  1. Worker无法访问:检查Worker是否已部署,网络连接是否正常
  2. 短链接不跳转:检查KV绑定是否正确,短码是否已存储
  3. 微信检测失效:检查User-Agent检测逻辑是否被微信更新影响
  4. 生成失败:检查输入URL格式,确保不是短链接本身

调试方法

  1. 使用浏览器开发者工具查看网络请求
  2. 查看Worker控制台的错误日志
  3. 测试不同的User-Agent模拟不同环境
  4. 检查KV命名空间中的数据状态

扩展建议

  1. 添加统计功能:记录每个短链接的访问次数
  2. 设置过期时间:为短链接添加有效期
  3. 密码保护:为敏感链接添加访问密码
  4. 批量生成:支持一次生成多个短链接
  5. API接口:提供RESTful API供第三方调用

后续可扩展方向

  • 添加 API Key 验证(防止滥用)
  • 支持自定义短码(如 /github
  • 添加点击统计(需额外 KV 或 D1)
  • 集成密码保护短链
  • 支持批量生成

总结

本教程详细介绍了如何使用Cloudflare Workers和Workers KV搭建一个完整的短网址服务。该方案具有部署简单、成本低廉、性能优越的特点,适合个人和小型项目使用。通过微信检测、重复URL检测、友好的用户界面等功能,提供了良好的用户体验。

这个短网址服务可以满足日常的链接缩短需求,同时具备良好的扩展性,可以根据需要进行功能增强和定制开发。