先看效果
你搭建好了之后:
原始链接:https://navbox.com.cn/project/cloudflare-short-url/
短链接: https://go.navbox.cn/abc123
点击短链接 → 302跳转 → 自动记录访问次数、IP、浏览器、来源。
全免费。不需要VPS,不需要数据库,不需要维护。
为什么自建
Bitly、TinyURL 这些用起来是方便。但:
- 免费版有链接数量限制
- 你不知道哪天服务就关了
- 数据在别人手里
- 自定义域名要付费
Cloudflare Workers 免费版每天10万次请求,对于个人站长来说根本用不完。
架构
用户访问短链接 → Cloudflare Workers → 查询KV存储 → 302跳转 → 记录访问日志
↓
Cloudflare KV
(key: 短码, value: 原始URL)
Cloudflare KV 是全球分布的键值数据库。读写都在边缘节点完成,延迟极低。
第一步:准备工作
你需要:
- 一个 Cloudflare 账号(免费)
- 一个域名(在 Cloudflare 上管理)
假设你的域名是 navbox.cn,准备用一个子域名 go.navbox.cn 来跑短链接服务。
先添加 DNS 记录:
类型: CNAME
名称: go
目标: 随便一个有效地址(Worker部署时会自动覆盖)
代理状态: 开启(橙色云朵)
第二步:创建 Worker
打开 Cloudflare Dashboard → Workers & Pages → 创建 Worker。
粘贴下面的代码:
// 短链接服务主程序
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
// KV 命名空间(部署时绑定)
// 在 wrangler.toml 中配置,或者在 Dashboard 中绑定
async function handleRequest(request) {
const url = new URL(request.url)
const shortCode = url.pathname.replace('/', '')
// 首页显示使用说明
if (!shortCode) {
return new Response(renderHomePage(), {
headers: { 'Content-Type': 'text/html; charset=utf-8' }
})
}
// API:创建短链接
if (url.pathname === '/api/create' && request.method === 'POST') {
return handleCreate(request)
}
// 查询短链接并跳转
const originalUrl = await SHORT_URL_KV.get(shortCode)
if (!originalUrl) {
return new Response('链接不存在或已过期', { status: 404 })
}
// 异步记录访问日志(不阻塞跳转)
recordVisit(shortCode, request)
// 302 跳转
return Response.redirect(originalUrl, 302)
}
// 创建短链接
async function handleCreate(request) {
const { url: targetUrl, customCode } = await request.json()
if (!targetUrl) {
return new Response(JSON.stringify({ error: '缺少URL参数' }), {
status: 400,
headers: { 'Content-Type': 'application/json' }
})
}
// 生成短码(6位随机字符串)
const code = customCode || generateShortCode(6)
// 检查是否已存在
const existing = await SHORT_URL_KV.get(code)
if (existing) {
return new Response(JSON.stringify({ error: '短码已存在,换个试试' }), {
status: 409,
headers: { 'Content-Type': 'application/json' }
})
}
// 存入 KV
await SHORT_URL_KV.put(code, targetUrl, {
expirationTtl: 0 // 永不过期
})
return new Response(JSON.stringify({
shortCode: code,
shortUrl: `https://go.navbox.cn/${code}`,
originalUrl: targetUrl
}), {
headers: { 'Content-Type': 'application/json' }
})
}
// 记录访问
async function recordVisit(shortCode, request) {
const cf = request.cf || {}
const visit = {
timestamp: new Date().toISOString(),
ip: request.headers.get('CF-Connecting-IP') || 'unknown',
country: cf.country || 'unknown',
ua: request.headers.get('User-Agent') || 'unknown',
referer: request.headers.get('Referer') || 'direct'
}
// 用 KV 存储访问记录(每天一份)
const today = new Date().toISOString().split('T')[0]
const logKey = `log:${shortCode}:${today}`
const existingLogs = await SHORT_URL_KV.get(logKey)
const logs = existingLogs ? JSON.parse(existingLogs) : []
logs.push(visit)
// 最多存最近1000条
if (logs.length > 1000) logs.shift()
await SHORT_URL_KV.put(logKey, JSON.stringify(logs))
// 更新计数器
const countKey = `count:${shortCode}`
const currentCount = parseInt(await SHORT_URL_KV.get(countKey) || '0')
await SHORT_URL_KV.put(countKey, String(currentCount + 1))
}
// 生成随机短码
function generateShortCode(length) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let result = ''
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return result
}
// 首页
function renderHomePage() {
return `<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>短链接服务</title>
<style>
body { font-family: -apple-system, sans-serif; max-width: 600px; margin: 50px auto; padding: 0 20px; }
input, button { font-size: 16px; padding: 10px; width: 100%; box-sizing: border-box; margin: 5px 0; }
.result { margin-top: 20px; padding: 15px; background: #f0fdf4; border-radius: 8px; display: none; }
.error { background: #fef2f2; }
</style>
</head>
<body>
<h1>🔗 短链接生成器</h1>
<input type="url" id="urlInput" placeholder="粘贴长链接..." />
<input type="text" id="customInput" placeholder="自定义后缀(选填,留空自动生成)" />
<button onclick="createLink()">生成短链接</button>
<div id="result" class="result"></div>
<script>
async function createLink() {
const url = document.getElementById('urlInput').value
const custom = document.getElementById('customInput').value
const result = document.getElementById('result')
if (!url) { alert('请输入URL'); return }
const res = await fetch('/api/create', {
method: 'POST',
body: JSON.stringify({ url, customCode: custom || undefined })
})
const data = await res.json()
result.style.display = 'block'
if (data.shortUrl) {
result.className = 'result'
result.innerHTML = '✅ 生成成功!<br><br>' +
'<strong>短链接:</strong><a href="' + data.shortUrl + '" target="_blank">' + data.shortUrl + '</a><br>' +
'<strong>原始链接:</strong>' + data.originalUrl
} else {
result.className = 'result error'
result.innerHTML = '❌ ' + (data.error || '生成失败')
}
}
</script>
</body>
</html>`
}
第三步:配置 KV + 绑定
在 Cloudflare Dashboard 左侧找到 Workers & Pages → KV:
- 点击「创建命名空间」,名称填
SHORT_URL_KV - 回到 Worker 编辑页面 →「设置」→「变量」
- 在「KV 命名空间绑定」中点击「添加绑定」
- 变量名称填
SHORT_URL_KV,选择刚才创建的 KV 命名空间 - 点击保存并部署
第四步:绑定自定义域名
在 Worker 详情页 →「触发器」→「自定义域名」:
域名: go.navbox.cn
Cloudflare 会自动配置 DNS 和 SSL 证书。等几分钟就能生效。
第五步:测试
打开 https://go.navbox.cn,应该能看到生成器页面。
输入一个长链接测试:
原始链接: https://github.com/cloudflare/workers-sdk
短链接: https://go.navbox.cn/aB3xK9
打开短链接 → 自动跳转到 GitHub。
第六步:用 curl 批量生成(适合脚本)
# 创建短链接
curl -X POST https://go.navbox.cn/api/create \
-H "Content-Type: application/json" \
-d '{"url": "https://navbox.com.cn/project/cloudflare-short-url/"}'
# 返回
{"shortCode":"xYz789","shortUrl":"https://go.navbox.cn/xYz789","originalUrl":"https://navbox.com.cn/project/cloudflare-short-url/"}
# 自定义后缀
curl -X POST https://go.navbox.cn/api/create \
-H "Content-Type: application/json" \
-d '{"url": "https://navbox.com.cn", "customCode": "navbox"}'
# 返回: https://go.navbox.cn/navbox
查看统计数据
访问计数存到了 KV 里。你可以通过 Worker 加一个统计 API:
// 在 handleRequest 中增加
if (url.pathname === '/api/stats/' + shortCode) {
const count = await SHORT_URL_KV.get('count:' + shortCode) || '0'
const today = new Date().toISOString().split('T')[0]
const logs = await SHORT_URL_KV.get('log:' + shortCode + ':' + today)
return new Response(JSON.stringify({
shortCode,
totalClicks: parseInt(count),
todayVisits: logs ? JSON.parse(logs).length : 0,
todayDetail: logs ? JSON.parse(logs) : []
}), {
headers: { 'Content-Type': 'application/json' }
})
}
curl https://go.navbox.cn/api/stats/aB3xK9
# 返回
{
"shortCode": "aB3xK9",
"totalClicks": 58,
"todayVisits": 12,
"todayDetail": [
{"timestamp": "2026-06-05T08:30:00Z", "ip": "xxx", "country": "CN", "ua": "Mozilla/5.0 ...", "referer": "direct"}
]
}
用 wrangler 部署(高级)
上面是在 Dashboard 里操作的。如果你想用命令行:
# 安装 wrangler
npm install -g wrangler
# 登录
wrangler login
# 初始化项目
mkdir my-short-url && cd my-short-url
wrangler init
# 粘贴上面的代码到 src/index.ts
# 配置 wrangler.toml
wrangler.toml 内容:
name = "short-url"
main = "src/index.ts"
compatibility_date = "2026-06-01"
[[kv_namespaces]]
binding = "SHORT_URL_KV"
id = "你的KV命名空间ID"
[env.production]
routes = [
{ pattern = "go.navbox.cn", custom_domain = true }
]
# 部署
wrangler deploy
优化方向
- 加密码保护:某些短链接只允许指定的人访问,加个密码验证
// 创建时传密码
await SHORT_URL_KV.put(code + ':password', hashedPassword)
// 访问时校验
if (needPassword) {
// 返回一个密码输入页面
}
- 过期时间:活动链接有时间限制
// 7天后自动过期
await SHORT_URL_KV.put(code, url, { expirationTtl: 604800 })
- 二维码生成:短链接自动生成二维码,用
api.qrserver.com或自己部署
// 在结果页面加上
<img src="https://api.qrserver.com/v1/create-qr-code/?data=${shortUrl}&size=200x200" />
- 批量导入:从 CSV 批量创建短链接
# 用脚本批量处理
cat links.csv | while IFS=, read url slug; do
curl -X POST https://go.navbox.cn/api/create \
-H "Content-Type: application/json" \
-d "{\"url\": \"$url\", \"customCode\": \"$slug\"}"
done
- Analytics 面板:用 D1 数据库(Cloudflare 的关系型数据库)替代 KV 做日志,支持时间范围查询和图表展示
总结
这个短链接服务:
- ✅ 完全免费(每月10万次请求)
- ✅ 全球加速(Cloudflare 边缘网络)
- ✅ 自带统计
- ✅ 支持自定义域名
- ✅ 有 API 可以对接其他工具
整个搭建过程不超过10分钟。不需要服务器、不需要数据库、不需要运维。
如果你需要更高级的功能——比如团队协作、AB测试、多域名支持——可以考虑用 Dub.co 或者自己基于这个 Worker 代码继续扩展。
代码就这么多。复制粘贴,改一下域名,跑起来。