How to Design a URL Shortener

Intermediate
9 min read· Architecture & Design

A URL shortener turns a long URL into a short code (bit.ly/abc123) and redirects visitors back to the original. It is a favourite system design interview question because it is simple to state but touches key ideas: generating unique short codes, a read-heavy redirect path, caching, database choice, and scaling to billions of links. The core is a key-value mapping from short code to long URL, optimised for very fast reads.

Think of it as a coat-check ticket

You hand over a long coat (the URL) and get a small numbered ticket (the short code). The ticket is tiny and easy to carry, but it maps directly back to your specific coat. Later you present the ticket and instantly get your coat back (the redirect). The whole system is just a very fast, very large ticket-to-coat lookup table — the art is generating unique tickets and returning coats at massive scale.

Step by Step

1 / 5

Key Concepts

Base62 Encoding

Encoding a numeric ID using 62 characters (0-9, a-z, A-Z) to produce a short, URL-safe code. A 7-character base62 code represents over 3 trillion links.

Distributed ID Generation

Generating unique IDs without a single-counter bottleneck — via Snowflake-style IDs, a ticket/range service, or per-node ID blocks — so code creation scales horizontally.

Read-Heavy Workload

Redirects dwarf creations, so the design optimises reads: caching, replicas, and a CDN. The write path can be simpler because it is comparatively rare.

301 vs 302 Redirect

A 301 (permanent) lets browsers and proxies cache the redirect, reducing load but hiding future analytics. A 302 (temporary) routes every click through your service so you can count clicks — a common trade-off.

Key Facts

  • Using an auto-increment ID plus base62 guarantees uniqueness without collision checks, but a single counter is a bottleneck — use a distributed ID scheme at scale.
  • A 301 permanent redirect is cacheable and cheap but prevents accurate click analytics; a 302 keeps every click flowing through your service.
  • The mapping is small and read-mostly, so aggressive caching gives most of the performance — the database is rarely the bottleneck once cached.

Real-World Applications

Link sharing and tracking

Marketing and social tools shorten URLs for clean sharing and to count clicks, using a 302 redirect through the service so every click is measured before forwarding the user.

Custom branded links

Allowing user-chosen aliases (yourbrand.co/sale) reuses the same mapping table with a uniqueness check on the alias, layering a product feature on the core design.

Frequently Asked Questions

How do you generate a unique short code?

Two common methods. One: hash the long URL (e.g., MD5) and take a short prefix, handling rare collisions by rehashing. Two (preferred): assign a unique incrementing ID and base62-encode it into a short string, which guarantees uniqueness with no collision checks. At scale, generate the IDs with a distributed scheme (Snowflake or a range service) to avoid a single-counter bottleneck.

What database should a URL shortener use?

Because access is a simple key lookup by short code, both an indexed relational table and a key-value store work well. The mapping is small and extremely read-heavy, so the more important decisions are aggressive caching (Redis), read replicas or a distributed KV store, and a CDN — rather than the specific database engine.

Should the redirect be a 301 or a 302?

A 301 permanent redirect is cacheable by browsers and proxies, reducing load, but it prevents you from counting every click because subsequent clicks skip your service. A 302 temporary redirect routes every click through your service, enabling accurate analytics at the cost of higher traffic. Choose based on whether caching or click tracking matters more.

How does a URL shortener scale to billions of links?

The redirect path is read-heavy, so scale reads: cache hot mappings in Redis, add read replicas or use a distributed key-value store, and front the service with a CDN and load balancer. For writes, use distributed ID generation so code creation is not bottlenecked by a single counter. The tiny, read-mostly data model makes this highly cacheable.

Related Topics