API-first URL shortener on Cloudflare Workers.
- Device targeting — different redirect URLs for desktop, tablet, phone
- Time-based links — schedule start/end times (ISO 8601)
- Multiple modes — direct redirect (302), splash page (countdown), password-protected
- Dynamic QR codes — SVG and PNG generation on-the-fly, no file storage
- Click analytics — device, OS, browser, IP, country, referrer tracking
- Auto slugs — 6-char nanoid, or bring your own custom slug
- Cloudflare Workers + Hono
- D1 (SQLite) — links & click logs
- KV — redirect cache (5min TTL, invalidated on update/delete)
- TypeScript
# Clone
git clone https://github.com/vibery-studio/cf-links.git
cd cf-links
# Install
npm install
# Create D1 database & KV namespace
npx wrangler d1 create shortlink-db
npx wrangler kv namespace create KV
# Update wrangler.toml with your database_id and KV id
# Run migration
npx wrangler d1 execute shortlink-db --local --file=src/db/schema.sql
# Set API token
npx wrangler secret put API_TOKEN
# Dev
npx wrangler dev
# Deploy
npx wrangler deployInteractive docs available at /api/docs/ui after deployment.
| Method | Path | Description |
|---|---|---|
| POST | /api/links |
Create link (returns short_url + qr_url) |
| GET | /api/links |
List links (?page=1&limit=20) |
| GET | /api/links/:slug |
Get link details |
| PUT | /api/links/:slug |
Update link |
| DELETE | /api/links/:slug |
Delete link |
| GET | /api/analytics/:slug |
Aggregated click stats |
| GET | /api/analytics/:slug/logs |
Raw click logs |
| Method | Path | Description |
|---|---|---|
| GET | /:slug |
Redirect / splash / password gate |
| GET | /qr/:slug |
QR code (?format=svg|png&size=300&download) |
curl -X POST https://your-worker.dev/api/links \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"url_phone": "https://m.example.com",
"slug": "my-link",
"mode": "redirect",
"start_at": "2026-01-01T00:00:00Z",
"end_at": "2026-12-31T23:59:59Z"
}'Response:
{
"slug": "my-link",
"short_url": "https://your-worker.dev/my-link",
"qr_url": "https://your-worker.dev/qr/my-link",
"mode": "redirect",
...
}| Mode | Behavior |
|---|---|
redirect |
302 redirect (default) |
splash |
HTML page with countdown timer, then redirect |
password |
Password form, redirect on correct password |
Generated dynamically — no files stored.
/qr/:slug → SVG (inline)
/qr/:slug?download → SVG (download)
/qr/:slug?format=png → PNG 300px
/qr/:slug?format=png&size=500 → PNG 500px
/qr/:slug?format=png&download → PNG (download)
MIT