Octonote AI
Make your own →

How a URL shortener works

Sunil Tiwari · Oct 12, 2026 · 2 min read

System design walkthrough: requirements, ID generation, caching and the redirect path.

A URL shortener turns https://example.com/a/very/long/path?ref=newsletter into sho.rt/aZ3kQ9x and redirects anyone who opens it. Simple to describe, and a great tour of read-heavy system design.

Requirements

  • Functional: create a short link, redirect to the original, optional custom alias and expiry.
  • Non-functional: redirects in under 50 ms at p99, highly available, links never collide.
  • Traffic: 100M new links a month, read:write ratio about 100:1.

Back-of-the-envelope

Writes per second, averaged over a month:

100×10630×24×3600≈40 writes/s⇒≈4,000 reads/s\frac{100 \times 10^{6}}{30 \times 24 \times 3600} \approx 40 \ \text{writes/s} \quad\Rightarrow\quad \approx 4{,}000 \ \text{reads/s}

How long must the code be? With the 62 characters [a-zA-Z0-9] and length kk we get 62k62^k codes:

627≈3.5×1012≫100M×12×10 years=1.2×101062^{7} \approx 3.5 \times 10^{12} \gg 100\text{M} \times 12 \times 10 \ \text{years} = 1.2 \times 10^{10}

So 7 characters last for centuries.

Generating the code

Hashing the long URL invites collisions. Instead, take a unique 64-bit ID from a counter service (or Snowflake-style IDs) and base62-encode it:

ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def base62(n: int) -> str:
    if n == 0:
        return ALPHABET[0]
    out = []
    while n:
        n, r = divmod(n, 62)
        out.append(ALPHABET[r])
    return "".join(reversed(out))

The redirect path

The hot path is a cache lookup. Only misses touch the database:

sequenceDiagram
  participant U as Browser
  participant A as App server
  participant C as Redis
  participant D as Database
  U->>A: GET /aZ3kQ9x
  A->>C: GET aZ3kQ9x
  alt cache hit
    C-->>A: long URL
  else miss
    A->>D: SELECT long_url
    D-->>A: long URL
    A->>C: SET aZ3kQ9x (TTL 24h)
  end
  A-->>U: 301 / 302 Location

Trade-offs worth discussing

  • 301 vs 302: 301 is cached by browsers (less load), 302 lets you count every click.
  • Counter as a single point of failure: hand out ID ranges in blocks of 1,000 per app server.
  • Hot links: a viral link is one cache key. Replicate it or keep it in an in-process LRU.
The canvas on the right is the whole architecture on one page. Drag it, zoom it, or open it full screen.

Published with Octonote AI · Make your own