URL Shortener

Design a system like bit.ly

Design: URL Shortener

Requirements

  • Functional: Shorten URL, redirect to original, custom aliases, expiration
  • Generate short URL for any long URL
  • Redirect short URL → original URL
  • Analytics: click count, referrers
  • Non-functional: ~100M URLs/month, low latency reads, high availability

URL Shortener Architecture

Short URL Generation

Use base62 encoding (a-z, A-Z, 0-9) of a unique counter/ID. 7 characters = 62^7 = 3.5 trillion URLs. To avoid sequential patterns, use a distributed counter (Snowflake ID) or hash-based approach.

typescript
// Core Logic
const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

function encode(num: number): string {
  let result = '';
  while (num > 0) {
    result = BASE62[num % 62] + result;
    num = Math.floor(num / 62);
  }
  return result.padStart(7, '0');
}

// Redirect: GET /:shortCode
// 1. Check Redis cache
// 2. If miss → query DB
// 3. Return 301 (permanent) or 302 (temporary) redirect
// 4. Async: log click to analytics queue

// Database schema
// urls: { id, short_code (indexed), original_url, created_at, expires_at, user_id }
// clicks: { id, short_code, timestamp, ip, user_agent, referrer }

Deep Dive Considerations

  • Use 301 (cacheable) vs 302 (not cached) redirect based on analytics needs
  • Rate limit URL creation to prevent abuse
  • Bloom filter to check if short code exists (avoid DB lookups)
  • Database: NoSQL (DynamoDB) for key-value lookups at scale
  • Cache: hot URLs in Redis with TTL
  • Analytics: async via Kafka → ClickHouse for fast aggregation