Design a URL shortener: from requirements to scale

The classic first case study. We design a service like bit.ly, covering ID generation, fast redirects, storage and scaling.

A long link shrinking into a short one

Goal: turn a long URL into a short one (sho.rt/aZ3k9) and redirect visitors who open it.

Requirements

  • Create a short link from a long URL. Optional custom alias and expiry.
  • Redirect fast and reliably.
  • Very read-heavy: far more redirects than creations.
  • Short links must be hard to guess in bulk if links are private.

Estimates

Assume 100 million new links per month and a 100:1 read ratio. That is about 40 writes/s and 4,000 redirects/s, with peaks maybe 3x. At about 500 bytes per record, 100 million links a month is roughly 50 GB a month, so a few terabytes over years. One database can hold that, but reads need caching.

API

POST /links {url, alias?, expiresAt?} returns the short link. GET /{code} returns a redirect.

Generating the short code

With 7 characters from base62 (a-z, A-Z, 0-9) there are 62^7, about 3.5 trillion codes, plenty.

  • Counter + base62: a global counter gives a unique number, encoded to base62. No collisions, but sequential codes are guessable, and one counter is a bottleneck. Fix: give each server a block of numbers to hand out.
  • Hash of the URL (take 7 chars): the same URL gives the same code, but collisions happen and need a retry.
  • Random code + unique check: simple. Insert with a unique constraint and retry on conflict.

Redirect: 301 or 302?

301 (permanent) lets browsers cache it, which saves load but hides later clicks from your analytics. 302 (temporary) hits your server each time, so you can count clicks and change the target. Choose based on whether you need analytics.

Data model

code (primary key), long_url, created_at, expires_at, owner_id. A key-value lookup by code. A relational database works fine, and a key-value store scales well.

Making reads fast

Put a cache (Redis) in front: code -> long_url. Popular links are served from memory. Add a CDN or edge function for global speed.

Extras

  • Analytics: log clicks to a queue and aggregate later, not in the redirect path.
  • Abuse: check URLs against malware and phishing lists, rate-limit creation, and block loops to your own domain.
  • Expiry: a background job deletes expired links.

Takeaway: the core is a key-value lookup. Pre-allocate ID blocks, cache hot links, keep analytics off the hot path, and protect against abuse.


System Design series, Part 3 (case studies): 1 of 16.

Keep reading

Also on Newssite