Blog19 min read
Build a link shortener with D1 and KV on Cloudflare
Deep D1 + KV link shortener guide - schema, hot cache redirects, uniqueness, abuse controls, analytics, and the Link Shortener experiment.
URL shorteners look trivial until you care about correctness under load: unique codes, fast redirects, analytics, and abuse controls. A proven Cloudflare pattern is:
- D1 as the source of truth for mappings
- KV as a hot cache for redirects
- A Worker that creates links and resolves them at the edge
The Link Shortener experiment implements that split so you can deploy a working shortener instead of reinventing the schema.
Cloudflare products in this pattern
| Product | Role | Docs |
|---|---|---|
| D1 | Durable mappings, uniqueness, listings | SQL database |
| D1 Workers API | env.DB.prepare | Worker binding |
| Workers KV | Hot redirect cache | KV |
| KV Workers API | get / put with TTL | API |
| Workers | Create + redirect routes | Runtime |
| Durable Objects (optional) | Strongly consistent click counters | State |
| Turnstile | Bot protection on public creates | Turnstile |
| Analytics Engine (optional) | Click analytics | Analytics |
| Queues (optional) | Async analytics fan-out | Queues |
Why D1 + KV together
| Concern | Store | Docs |
|---|---|---|
| Durable mapping, uniqueness, listings | D1 | D1 |
| Ultra-hot redirects | KV cache of D1 rows | KV |
| Click counters with strong consistency | Optional Durable Object | Durable Objects |
KV alone is awkward for “list my links” and uniqueness constraints. D1 alone is fine for low QPS, but KV shines when the same slug is hit constantly worldwide. Official primers: D1 get started, KV get started.
Request flows
Create
POST { url } → validate → insert D1 → optionally warm KV → return codeUse transactions / unique constraints in D1 so two creates cannot race the same custom code (D1 API).
Resolve
GET /:code → KV get → on miss, D1 select → fill KV → 302 LocationAlways validate destination URLs (http/https only) to avoid open redirects into dangerous schemes - same discipline as other catalog URL APIs.
Schema sketch
Minimum viable tables:
links(code PRIMARY KEY, url TEXT NOT NULL, created_at, expires_at, owner_id)- Optional
clicks(code, ts, colo, asn)or push clicks to Analytics Engine / Queues
Explore SQL interactively with D1 SQL Playground while you iterate. For simpler key/value notes without SQL, see KV Notes.
Abuse and product extras
- Rate-limit creates (Rate Limiter Demo, WAF rate limiting)
- Turnstile on public create forms (Turnstile Verify)
- Expiration columns in D1 + KV TTLs that match
- Custom codes with reservation rules
- Soft-delete vs hard-delete for audit trails
- Strongly consistent counters via Durable Counter patterns when KV increments are too lossy
Consistency notes
KV is eventually consistent (KV consistency). After create:
- Warm KV immediately from the create path
- Accept that a redirect MISS may hit D1 briefly after write
- On update/delete, delete the KV key explicitly
D1 read replicas and latency characteristics are covered in D1 read replication - design list UIs accordingly.
Deploy Link Shortener
- Provision D1 + KV (bindings)
- Deploy Link Shortener
- Create a link, resolve it twice, confirm cache behavior
- Add Turnstile / auth before exposing create publicly
- Add analytics when product metrics matter
You get a real two-storage edge pattern - not a toy in-memory map - that matches how many production Cloudflare apps actually split durability (D1) and speed (KV).
Production checklist
- Unique constraint on codes in D1.
- Warm KV on create; delete KV keys on update/delete.
- Validate destinations (
http/httpsonly). - Protect create with auth + Turnstile.
- Align KV TTLs with link expiration columns.
- Decide analytics path (D1 rows vs Analytics Engine vs Queues).
FAQ
KV said miss right after create - is D1 wrong?
KV is eventually consistent. Warm on write and tolerate a brief D1 fallback.
Related experiments
These deployable references implement the patterns in this article. Open the docs for API details, then Deploy or clone the source.
- Link Shortener - URL shortener with Cloudflare D1 + KV - create short links, redirect with edge caching, and track clicks
- D1 SQL Playground - Read-only SQL playground over a seeded Cloudflare D1 database with safe SELECT validation - learn D1 at the edge
- KV Notes - Simple note CRUD with Workers KV at the edge - minimal key-value storage reference for Cloudflare Workers
- Durable Counter - Canonical Durable Objects counter - globally consistent increment/decrement with strong consistency at the edge
- Rate Limiter Demo - Native Workers Rate Limiting binding with 429 responses and usage visualization - protect APIs at the edge without Redis
- Turnstile Verify - Verify Cloudflare Turnstile tokens via the siteverify API - bot protection for forms and APIs on Workers