Workers + KV 存储实现自建短链服务:从入门到实战

我为什么要自建短链服务?
说实话,我之前一直用某个第三方短链服务,用了快两年。结果有一天早上起来,发现所有的短链全都失效了——那个服务商突然宣布关闭了。我在社交媒体上分享的几百条链接,全部变成 404。 那一刻我就在想,能不能搞一个完全属于自己的短链服务?数据自己掌控,想怎么定制就怎么定制,再也不用担心第三方跑路。 后来我发现 Cloudflare Workers + KV 存储简直完美契合这个需求:
- 免费额度超级大(每天 10 万次请求)
- 全球 200+ 节点,访问速度飞快
- 部署简单,几行代码就能跑起来
- 数据完全可控,想存多久存多久 这篇文章就来分享一下,我是怎么用 Workers + KV 搭建自己的短链服务的,包括自定义短码、访问统计等功能。代码我都会贴出来,照着做基本半小时就能上线。
为什么选择 Workers + KV?
Cloudflare Workers 是什么?
简单理解,Workers 就是运行在 Cloudflare 边缘网络上的 Serverless 函数。你写好代码,它会自动部署到全球 200 多个节点,用户访问时会自动路由到最近的节点,延迟超低。 重点是,免费额度非常慷慨:
- 每天 10 万次请求
- 每次请求 10ms CPU 时间
- 对于个人使用或小团队,基本够用
KV 存储的优势
KV(Key-Value)存储是 Cloudflare 提供的分布式键值数据库,专门为边缘计算优化:
- 读取超快:中位数 12ms,因为数据会缓存在边缘节点
- 全球同步:写入后 60 秒内会同步到全球所有节点
- 免费额度:每天 10 万次读取、1000 次写入 对于短链服务来说,KV 简直是天生绝配:
- 短码作为 key,原始 URL 作为 value
- 读多写少的场景(短链创建少,访问多)
- 全球分布,任何地方访问都快
对比第三方短链服务
| 特性 | 第三方短链 | 自建 Workers + KV |
|---|---|---|
| 数据控制 | 数据在第三方 | 完全可控 |
| 定制化 | 功能固定 | 随意定制 |
| 稳定性 | 可能跑路 | Cloudflare 背书 |
| 广告 | 可能有跳转页 | 无广告 |
| 成本 | 可能收费 | 基本免费 |
| 访问速度 | 取决于服务商 | 全球边缘网络 |
从零开始搭建短链服务
好了,理论说够了,开始实操吧。
准备工作
1. 注册 Cloudflare 账号 去 cloudflare.com 注册一个账号,免费的就够用。 2. 安装 Wrangler CLI Wrangler 是 Cloudflare 官方的命令行工具,用来管理 Workers 项目。
npm install -g wrangler
# 或者用 yarn
yarn global add wrangler安装完成后,登录你的 Cloudflare 账号:
wrangler login这会打开浏览器让你授权,点击允许就行。 3. 创建项目
mkdir my-shortlink
cd my-shortlink
wrangler init按照提示操作,选择创建一个 JavaScript 项目(当然你也可以用 TypeScript)。
Step 1:创建 KV 命名空间
KV 存储需要先创建一个”命名空间”(Namespace),可以理解为数据库里的一张表。 运行以下命令:
# 创建生产环境的 KV 命名空间
wrangler kv namespace create SHORTLINKS
# 创建预览环境的 KV 命名空间(用于本地测试)
wrangler kv namespace create SHORTLINKS --preview命令执行后,会返回两个 ID,类似这样:
{ binding = "SHORTLINKS", id = "abc123..." }
{ binding = "SHORTLINKS", preview_id = "def456..." }重要:把这两个 ID 记下来,等下要用。 然后编辑 wrangler.toml 文件,添加 KV 绑定:
name = "my-shortlink"
main = "src/index.js"
compatibility_date = "2025-12-01"
# KV 命名空间绑定
kv_namespaces = [
{ binding = "SHORTLINKS", id = "你的 production ID", preview_id = "你的 preview ID" }
]这里的 binding = "SHORTLINKS" 意思是,在代码里可以通过 env.SHORTLINKS 来访问这个 KV 存储。
Step 2:实现基础短链功能
现在开始写核心代码。打开 src/index.js,写入以下内容:
export default {
async fetch(request, env) {
const url = new URL(request.url);
const path = url.pathname.slice(1); // 去掉开头的 /
// 处理根路径
if (path === '') {
return new Response('欢迎使用短链服务!', { status: 200 });
}
// GET 请求:短链重定向
if (request.method === 'GET') {
// 从 KV 中查询短码对应的原始 URL
const targetUrl = await env.SHORTLINKS.get(path);
if (targetUrl) {
// 找到了,301 重定向
return Response.redirect(targetUrl, 301);
} else {
// 没找到,返回 404
return new Response('短链不存在', { status: 404 });
}
}
// POST 请求:创建短链
if (request.method === 'POST') {
try {
const body = await request.json();
const { url: targetUrl, code } = body;
// 基本校验
if (!targetUrl) {
return new Response('缺少 url 参数', { status: 400 });
}
// 生成短码
const shortCode = code || generateRandomCode();
// 检查短码是否已存在
const existing = await env.SHORTLINKS.get(shortCode);
if (existing) {
return new Response('短码已存在', { status: 409 });
}
// 存入 KV
await env.SHORTLINKS.put(shortCode, targetUrl);
// 返回结果
return new Response(JSON.stringify({
shortCode,
shortUrl: `${url.origin}/${shortCode}`,
targetUrl
}), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response('请求格式错误', { status: 400 });
}
}
// 其他请求方法不支持
return new Response('方法不允许', { status: 405 });
}
};
// 生成随机短码(6位字母数字组合)
function generateRandomCode(length = 6) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let code = '';
for (let i = 0; i < length; i++) {
code += chars.charAt(Math.floor(Math.random() * chars.length));
}
return code;
}代码解释:
GET请求:用户访问yourdomain.com/abc123,从 KV 中查询abc123对应的原始 URL,然后 301 重定向过去POST请求:接收url和可选的code参数,如果没提供code就随机生成一个,然后存入 KVgenerateRandomCode:生成 6 位随机字母数字组合
Step 3:本地测试
代码写好后,先在本地测试一下:
wrangler dev这会启动一个本地服务器,通常是 http://localhost:8787。 测试创建短链:
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'返回:
{
"shortCode": "aBc123",
"shortUrl": "http://localhost:8787/aBc123",
"targetUrl": "https://example.com"
}测试访问短链: 在浏览器打开 http://localhost:8787/aBc123,应该会重定向到 https://example.com。 如果一切正常,说明基础功能已经 OK 了!
Step 4:添加自定义短码支持
上面的代码其实已经支持自定义短码了,只要在 POST 请求里带上 code 参数就行:
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com", "code": "my-link"}'但为了更健壮,我们可以加一些校验:
// 在 POST 请求处理部分,生成短码之前加上这段
// 如果用户提供了自定义短码,校验格式
if (code) {
// 只允许字母、数字、连字符
if (!/^[a-zA-Z0-9-]+$/.test(code)) {
return new Response('短码格式不正确(仅支持字母、数字、连字符)', { status: 400 });
}
// 长度限制
if (code.length < 3 || code.length > 20) {
return new Response('短码长度必须在 3-20 之间', { status: 400 });
}
}这样就能防止用户创建一些奇怪的短码,比如包含特殊字符或者太长。
Step 5:实现访问统计
很多时候,我们不只想要短链功能,还想知道每个链接被访问了多少次。 思路:
- 每次访问短链时,除了重定向,还要把访问次数 +1
- 统计数据也存在 KV 里,key 格式为
stats:{shortCode}修改 GET 请求部分的代码:
// GET 请求:短链重定向
if (request.method === 'GET') {
const targetUrl = await env.SHORTLINKS.get(path);
if (targetUrl) {
// 异步更新访问统计(不阻塞重定向)
const statsKey = `stats:${path}`;
// 后台更新统计,不影响重定向速度
env.SHORTLINKS.get(statsKey).then(count => {
const newCount = (parseInt(count) || 0) + 1;
env.SHORTLINKS.put(statsKey, newCount.toString());
});
return Response.redirect(targetUrl, 301);
} else {
return new Response('短链不存在', { status: 404 });
}
}添加查询统计的接口:
// 在 GET 请求处理前,加上这个判断
if (path.startsWith('stats/')) {
const shortCode = path.slice(6); // 去掉 stats/ 前缀
const statsKey = `stats:${shortCode}`;
const count = await env.SHORTLINKS.get(statsKey);
return new Response(JSON.stringify({
shortCode,
visits: parseInt(count) || 0
}), {
headers: { 'Content-Type': 'application/json' }
});
}现在可以通过访问 http://localhost:8787/stats/abc123 来查询某个短链的访问次数。 注意:KV 不支持原子操作,高并发下访问统计可能不准确。如果需要精确统计,应该用 Durable Objects。但对于大部分个人使用场景,这个方案够用了。
Step 6:部署到生产环境
测试没问题后,就可以部署到 Cloudflare 的全球网络了:
wrangler deploy部署成功后,Wrangler 会给你一个 URL,类似 https://my-shortlink.your-subdomain.workers.dev。 这个 URL 就是你的短链服务地址,全球访问,速度飞快。 绑定自定义域名(可选): 如果你有自己的域名(比如 short.example.com),可以在 Cloudflare Dashboard 里绑定:
- 进入 Workers & Pages 页面
- 选择你的 Worker
- 点击 Settings > Triggers
- 添加 Custom Domain 绑定后,就可以用自己的域名访问短链了,比如
https://short.example.com/abc123。
进阶功能
基础功能已经搞定了,但如果你想更进一步,还可以加这些功能:
1. 批量创建短链
有时候需要一次性创建多个短链,可以加一个批量接口:
// 在 POST 请求处理部分,添加批量创建逻辑
if (request.method === 'POST' && url.pathname === '/batch') {
try {
const body = await request.json();
const links = body.links; // 格式:[{url, code?}, ...]
if (!Array.isArray(links)) {
return new Response('links 必须是数组', { status: 400 });
}
const results = [];
for (const link of links) {
const { url: targetUrl, code } = link;
const shortCode = code || generateRandomCode();
// 检查是否已存在
const existing = await env.SHORTLINKS.get(shortCode);
if (!existing) {
await env.SHORTLINKS.put(shortCode, targetUrl);
results.push({ shortCode, targetUrl, success: true });
} else {
results.push({ shortCode, targetUrl, success: false, error: '短码已存在' });
}
}
return new Response(JSON.stringify({ results }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response('请求格式错误', { status: 400 });
}
}调用方式:
curl -X POST http://localhost:8787/batch \
-H "Content-Type: application/json" \
-d '{
"links": [
{"url": "https://example1.com", "code": "link1"},
{"url": "https://example2.com"}
]
}'2. 设置过期时间
KV 支持 TTL(Time To Live),可以让短链自动过期:
// 在存入 KV 时,添加 expirationTtl 参数
await env.SHORTLINKS.put(shortCode, targetUrl, {
expirationTtl: 86400 // 24小时后自动删除,单位:秒
});如果想让用户自定义过期时间:
const { url: targetUrl, code, ttl } = body;
const options = {};
if (ttl) {
options.expirationTtl = parseInt(ttl);
}
await env.SHORTLINKS.put(shortCode, targetUrl, options);3. 访问控制
如果不想让所有人都能创建短链,可以加一个简单的 API Token 验证:
// 在 wrangler.toml 里添加环境变量
# [vars]
# API_TOKEN = "your-secret-token"
// 在 POST 请求处理前,添加验证
if (request.method === 'POST') {
const token = request.headers.get('Authorization');
if (token !== `Bearer ${env.API_TOKEN}`) {
return new Response('未授权', { status: 401 });
}
// ... 后续创建短链逻辑
}调用时带上 token:
curl -X POST http://localhost:8787 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-secret-token" \
-d '{"url": "https://example.com"}'4. 防止滥用(Rate Limiting)
为了防止有人恶意创建大量短链,可以加一个简单的频率限制:
// 使用 IP 地址作为限流标识
const clientIp = request.headers.get('CF-Connecting-IP');
const rateLimitKey = `ratelimit:${clientIp}`;
// 获取当前计数
const count = await env.SHORTLINKS.get(rateLimitKey);
if (parseInt(count) >= 10) {
return new Response('请求过于频繁,请稍后再试', { status: 429 });
}
// 计数 +1,设置 1 小时过期
const newCount = (parseInt(count) || 0) + 1;
await env.SHORTLINKS.put(rateLimitKey, newCount.toString(), {
expirationTtl: 3600 // 1小时
});这个方法会限制每个 IP 每小时最多创建 10 个短链。
性能优化和最佳实践
性能优化技巧
1. 缓存策略 KV 的读取本身就很快(中位数 12ms),但如果想更快,可以在 Worker 内存中加一层缓存:
// 使用 Map 作为简单的内存缓存
const cache = new Map();
const targetUrl = cache.get(path) || await env.SHORTLINKS.get(path);
if (targetUrl) {
cache.set(path, targetUrl);
return Response.redirect(targetUrl, 301);
}不过要注意,Worker 的内存不是持久化的,重启后会丢失。 2. 减少 KV 写入次数 KV 的免费额度是每天 1000 次写入,如果访问统计写入太频繁,可能会超额。 解决方案:
- 使用 Durable Objects 来处理统计(支持原子操作)
- 或者每 N 次访问才写入一次 KV
- 或者使用 Cloudflare Analytics Engine 3. CORS 配置 如果你的短链服务要给前端调用,别忘了加 CORS 头:
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// OPTIONS 请求处理
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
// 在返回响应时添加 CORS 头
return new Response(body, {
headers: { ...headers, ...corsHeaders }
});成本控制
Cloudflare Workers 的免费额度非常慷慨,但还是要注意: 免费额度:
- 每天 10 万次请求
- KV 每天 10 万次读取、1000 次写入
- 每次请求 10ms CPU 时间 超出免费额度的成本(Workers Paid 计划,$5/月起):
- 每百万次请求 $0.50
- KV 读取每百万次 $0.50
- KV 写入每百万次 $5.00
- KV 存储每 GB/月 $0.50 对于个人使用,基本不会超出免费额度。就算是小团队,每天几万次访问也够用。 节省请求数的技巧:
- 使用 301 重定向(浏览器会缓存)而不是 302
- 静态资源(如管理页面)用 Workers Pages 托管,不占用 Worker 请求数
- 合理设置 TTL,让过期链接自动清理
安全考虑
1. 防止恶意短链 如果你的短链服务对外开放,可能会被人用来缩短恶意网站链接。 建议:
- 加入 API Token 验证
- 使用黑名单过滤已知的恶意域名
- 记录创建者 IP,方便追溯 2. 防止短码碰撞 虽然 6 位字母数字组合有 62^6 ≈ 568 亿种可能,碰撞概率很低,但还是要检查:
// 创建短链时,检查是否已存在
const existing = await env.SHORTLINKS.get(shortCode);
if (existing) {
return new Response('短码已存在', { status: 409 });
}3. 限制目标 URL 可以加一个白名单,只允许重定向到特定域名:
const allowedDomains = ['example.com', 'mywebsite.com'];
const targetDomain = new URL(targetUrl).hostname;
if (!allowedDomains.some(d => targetDomain.endsWith(d))) {
return new Response('不允许的目标域名', { status: 403 });
}我的实际使用体验
搭建完成后,我已经用了好几个月,分享一下实际体验: 优点:
- 真的快:全球访问延迟基本在 50ms 以内,比之前用的第三方服务快很多
- 稳定:Cloudflare 的网络非常稳定,基本没遇到过宕机
- 省心:部署后不用管,自动扩容,不用担心流量暴增
- 免费:我每天的请求量大概在几千次,完全在免费额度内 小坑:
- KV 写入延迟:KV 是最终一致性的,写入后可能要等几十秒才能全球同步。不过对于短链场景影响不大,毕竟创建短链后不会立刻就有人访问
- 统计不精确:KV 不支持原子操作,高并发下统计会有误差。如果需要精确统计,得用 Durable Objects,但那个超出免费额度后会贵一些 后续计划:
- 加一个简单的 Dashboard,用 Workers Pages 搭建,可以可视化管理短链
- 接入 Cloudflare Analytics,看详细的访问数据(来源、地域等)
- 加上二维码生成功能,方便线下分享
总结
用 Cloudflare Workers + KV 搭建短链服务,真的是又快又省钱。核心代码不到 100 行,部署只要一条命令,关键是数据完全在自己手里,再也不用担心第三方跑路。 如果你也有类似的需求,强烈建议动手试试。代码我都贴出来了,照着敲一遍,半小时就能上线。 核心步骤回顾:
- 注册 Cloudflare 账号,安装 Wrangler
- 创建 KV 命名空间,配置 wrangler.toml
- 写代码处理 GET(重定向)和 POST(创建短链)请求
- 本地测试,部署到生产 后续可以慢慢加功能,比如统计、批量创建、过期时间等。反正代码在自己手里,想怎么改就怎么改,这种感觉真的很爽。 有问题欢迎留言交流,祝你也能搭建出自己的短链服务!
发布于: 2025年12月1日 · 修改于: 2025年12月4日


