A blazingly fast, production-grade URL shortener built on Cloudflare Workers + D1 + KV, using Hono. Create short URLs, redirect at the edge, and track per-click analytics.
- Edge cache (KV). The redirect hot path (
GET /:id) is read-through cached in Workers KV — warm short-codes resolve globally at the edge without touching D1. - Non-blocking analytics. Click records are written via
waitUntil, so the redirect returns immediately and never waits on a D1 write. - D1 (SQLite). Source of truth for links, analytics, and admin sessions, with indexes backing dedup and per-link analytics queries.
- Admin dashboard. A React + Vite SPA served by the Worker at
/admin, behind session-cookie auth (see below). - Rate limiting on
POST /createand admin login, link expiry / soft-disable, constant-time auth.
src/
index.ts App wiring, CORS, rate limit, assets routing, error handling
types.ts Bindings + shared types
lib/ auth, admin-auth, session, cache (KV), links, id, validation, url, responses
routes/ redirect, create, analytics, admin (dashboard API), assets (SPA)
openapi/spec.ts Swagger spec
admin/ React + Vite + Tailwind admin dashboard (builds to ../dist-admin)
migrations/ D1 migrations (0001_init, 0002_perf_expiry, 0003_sessions)
test/ Vitest (pool-workers) suite
A password-protected, single-user dashboard at https://<domain>/admin:
- Auth — session cookie (
__Host-session, HttpOnly, Secure, SameSite=Lax). The token is stored in D1 as a SHA-256 hash; a synchronizer CSRF token is required (X-CSRF-Token) on all mutations. Login is rate-limited. - Sessions — every login records IP, user-agent, and country; active sessions are listed and individually revocable.
- Features — overview (clicks trend, top links/countries/referrers), link management (create / search / toggle / expiry / delete), per-link click records with search, all fully mobile-responsive.
Credentials come from secrets (ADMIN_USERNAME, ADMIN_PASSWORD).
- Cloudflare Workers account
- D1 Database + KV Namespace
- Node.js 20+
git clone https://github.com/belphegor-s/url-shortner
cd url-shortner
npm install# D1 — set the printed database_id in wrangler.jsonc
wrangler d1 create url_shortener_db
# KV — set the printed id in wrangler.jsonc (replaces REPLACE_WITH_LINKS_KV_ID)
wrangler kv namespace create LINKS_KVUpdate wrangler.jsonc with both ids:
The programmatic analytics API uses a bearer token (API_KEY); the dashboard uses ADMIN_USERNAME / ADMIN_PASSWORD.
wrangler secret put API_KEY
wrangler secret put ADMIN_USERNAME
wrangler secret put ADMIN_PASSWORDFor local dev, create .dev.vars (gitignored):
API_KEY=local-dev-secret
ADMIN_USERNAME=ayush
ADMIN_PASSWORD=local-dev-password
npm run migrate:local # local D1
npm run migrate # remote D1wrangler login
npm run admin:install # install dashboard deps (once)
# Local dev — two servers:
npm run dev # Worker API on :8787
npm run dev:admin # dashboard on :5173 (proxies /api to the Worker)
# Deploy (predeploy builds the dashboard into dist-admin automatically):
npm run deployThe dashboard is reachable at /admin. In local dev, open the Vite server (http://localhost:5173/admin); in production it is served by the Worker from the ASSETS binding.
JSON body:
{
"url": "https://example.com",
"custom_id": "mycode", // optional, [a-zA-Z0-9_-]{1,64}, not a reserved word
"expires_in": 86400 // optional seconds; or "expires_at": "2026-12-31T23:59:59Z"
}Response 201:
{
"short_url": "https://short.procd.cc/mycode",
"id": "mycode",
"expires_at": null,
"existing": false
}Posting an already-shortened URL (no custom_id) returns the existing link with existing: true. Rate limited per client IP.
302 to the original URL and records a click (IP, user-agent, country, referrer). Returns 404 for unknown codes, 410 for expired/inactive links.
Query params: page, limit (default 50, max 500), sort (asc/desc).
JSON body: { "ids": ["abc123", "xyz456"] }. Also evicts the KV cache.
Most recent 1000 clicks for one short code.
Auth: protected endpoints require Authorization: Bearer <API_KEY>.
npm test # Vitest (Cloudflare Workers pool)
npm run typecheck # tsc --noEmitInteractive docs at the root route / — live at short.procd.cc.
MIT — see LICENSE.