Engineering guide
URL shortener system design
Shortening a URL is a hash-map lookup with a web server in front of it. Everything hard about the problem lives in the details: making slugs unique without a bottleneck, keeping redirects under 50 ms worldwide, and counting clicks without slowing anyone down. This is how we build it at SwiftLink.
The request path
GET /spring24
│
├─► edge node ──► cache hit? ──yes──► 302 to destination (~10 ms)
│ │no
│ ▼
│ database lookup by slug
│ │
│ ├─► not found ──► 404 page
│ └─► found ──────► warm cache ──► 302 to destination
│
└─► async: enqueue click event {slug, ts, ref, ua, country}
│
▼
clicks table ──► aggregated for analyticsThe critical rule: nothing that can fail slowly belongs in the redirect path. Click recording is fire-and-forget; if the analytics write fails, the user still lands on the destination.
Slug generation
Base62 (a-zA-Z0-9) is the standard alphabet: URL-safe, case-dense, no encoding surprises. Two strategies:
Counter + base62
Encode a monotonically increasing ID. Guaranteed unique with no retries and the shortest possible slug — but sequential slugs are enumerable, so anyone can walk your entire link set.
Random + unique index
Generate 7 random base62 characters and insert against a unique constraint, retrying on conflict. Not enumerable, trivially horizontally scalable, and collisions stay rare well past a billion links.
We use random slugs, plus a reserved-word list so a generated slug can never shadow an application route like /pricing or /auth.
301 vs 302
A 301 tells the browser the move is permanent, so it caches the redirect and stops asking your server — which means your click count silently stops growing. A 302 costs one round trip per click and keeps analytics accurate. Any shortener whose value is measurement should use 302 (or 307 to preserve the request method) and rely on edge caching for speed instead.
Storing clicks
Raw click rows grow fast. Keep the write path append-only and narrow — slug, timestamp, referrer, user agent, country — then roll rows up into hourly and daily aggregates for the dashboard. Derive device and browser at write time so charts never parse user agents at read time. Never store raw IP addresses; hash or truncate them for unique-visitor counting so you stay privacy-compliant.
Capacity sketch
| Metric | Estimate |
|---|---|
| Read : write ratio | ~100 : 1 — reads dominate, cache aggressively |
| Row size per link | ~200 bytes — 1B links is only ~200 GB |
| Row size per click | ~100 bytes — aggregate and expire raw rows |
| Slug space (7 chars, base62) | ~3.5 trillion |
| Target redirect latency | < 50 ms at p95 worldwide |
Frequently asked questions
How do you generate short URL slugs?
Two common approaches: encode an auto-incrementing ID in base62, which guarantees uniqueness and produces the shortest possible slug, or generate a random 6-8 character base62 string and retry on a unique-constraint violation. Random slugs avoid leaking how many links exist.
How many links can a 7-character slug represent?
Base62 with 7 characters gives 62^7, roughly 3.5 trillion combinations. At a million new links per day it would take thousands of years to exhaust, and collision probability stays negligible with a retry on conflict.
Should a URL shortener use a 301 or 302 redirect?
301 is cacheable and fastest for the user, but browsers cache it, so subsequent visits never reach your server and clicks go uncounted. 302 keeps every click measurable at the cost of a round trip. Analytics-first shorteners use 302; pure vanity redirects use 301.
How do you keep redirects fast at scale?
Serve redirects from edge locations, cache the slug-to-destination mapping in memory or a KV store, and write click events asynchronously so analytics never sits in the redirect path.
Skip the build
If you would rather ship than operate a redirect tier, Shortly exposes the same primitives through a REST API — see the developer docs or the Short.io comparison.
Create a link in one request
Paste a long URL and get a working short link instantly. Create a free account to track clicks.