BetterLink Logo 比邻
切换语言
切换主题

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

Cloudflare 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;
}

代码解释

  1. GET 请求:用户访问 yourdomain.com/abc123,从 KV 中查询 abc123 对应的原始 URL,然后 301 重定向过去
  2. POST 请求:接收 url 和可选的 code 参数,如果没提供 code 就随机生成一个,然后存入 KV
  3. generateRandomCode:生成 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 里绑定:

  1. 进入 Workers & Pages 页面
  2. 选择你的 Worker
  3. 点击 Settings > Triggers
  4. 添加 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 行,部署只要一条命令,关键是数据完全在自己手里,再也不用担心第三方跑路。 如果你也有类似的需求,强烈建议动手试试。代码我都贴出来了,照着敲一遍,半小时就能上线。 核心步骤回顾

  1. 注册 Cloudflare 账号,安装 Wrangler
  2. 创建 KV 命名空间,配置 wrangler.toml
  3. 写代码处理 GET(重定向)和 POST(创建短链)请求
  4. 本地测试,部署到生产 后续可以慢慢加功能,比如统计、批量创建、过期时间等。反正代码在自己手里,想怎么改就怎么改,这种感觉真的很爽。 有问题欢迎留言交流,祝你也能搭建出自己的短链服务!

发布于: 2025年12月1日 · 修改于: 2025年12月4日

相关文章