Skip to main content

Design a URL Shortener

Design a URL shortening service that converts long URLs into short, shareable links and redirects users to the original URLs.

Related Concepts: Base62 Encoding, Database Sharding, Caching (Redis/Memcached), Load Balancing, Unique ID Generation, CAP Theorem, Read-Heavy Optimization, TTL/Expiration

Step 1: Requirements and Scope

Functional Requirements

  • Given a long URL, generate a unique short URL
  • Given a short URL, redirect to the original long URL
  • Support custom short aliases (optional)
  • Support URL expiration (optional)
  • Track click analytics (optional)

Non-Functional Requirements

RequirementTargetRationale
Availability99.99%Broken links = lost users and revenue
Latency< 100ms redirectUsers expect instant redirects
DurabilityNever lose URLsLinks shared widely must always work
ScalabilityBillions of URLsService grows over time
SecurityNon-guessable URLsPrevent enumeration attacks

Scale Estimation

  • 100 million new URLs per month (~40 URLs/second)
  • 10:1 read to write ratio -> 1 billion redirects/month (~400 redirects/second)
  • Average URL length: 200 bytes
  • Storage for 5 years: 100M x 12 x 5 x 200 bytes = 1.2 TB (excluding metadata)

How Many Characters for Short Code?

CharactersBase62 CombinationsYears to Exhaust (at 100M/month)
656.8 billion47 years
73.5 trillion2,900 years
8218 trillion181,000 years

Recommendation: 7 characters provides ample headroom

Step 2: High-Level Design

Loading diagram...

Step 3: API Design

Create Short URL

FieldTypeRequiredDescription
long_urlstringYesOriginal URL to shorten
custom_aliasstringNoUser-specified short code
expires_attimestampNoWhen the link should expire

Response: Short URL with metadata

Redirect Flow

Loading diagram...

301 vs 302 Redirects

Redirect TypeBrowser BehaviorAnalyticsSEO
301 PermanentBrowser caches, skips serverUndercounts (cached)Passes link juice
302 TemporaryAlways hits serverAccurate trackingLink juice may not pass

Recommendation: Use 301 for performance, 302 if analytics are critical

Step 4: Short Code Generation Strategies

Strategy Comparison

StrategyUniquenessPredictabilityCoordinationComplexity
MD5/SHA HashNeeds collision handlingDeterministicNoneLow
Counter (Auto-increment)Guaranteed uniqueVery predictableCentral counterLow
Random GenerationNeeds collision checkUnpredictableNoneLow
UUID to Base62Very likely uniqueUnpredictableNoneLow
Snowflake IDGuaranteed uniqueSemi-predictableMinimalMedium
Pre-generated PoolGuaranteed uniqueUnpredictablePool managementMedium

Strategy 1: Hash-Based

Loading diagram...

Pros: Same URL produces same code (deduplication possible) Cons: Collisions require handling, predictable pattern

Strategy 2: Counter-Based (Distributed)

Loading diagram...

Pros: No collisions, no DB lookup for uniqueness Cons: Sequential patterns reveal traffic volume

Loading diagram...
ComponentBitsPurpose
Sign bit1Always 0 (positive number)
Timestamp41Milliseconds since epoch (69 years)
Machine ID101024 unique machines
Sequence124096 IDs per millisecond per machine

Result: ~4 million IDs/second per machine, globally unique, no coordination

Step 5: Database Design

Schema Design

ColumnTypeIndexNotes
idBIGINTPrimary KeySnowflake ID
short_codeVARCHAR(10)Unique IndexThe short URL code
long_urlTEXTNoneOriginal URL (could be very long)
user_idBIGINTIndexOwner of the URL (if authenticated)
created_atTIMESTAMPNoneFor analytics
expires_atTIMESTAMPIndexFor cleanup jobs
click_countBIGINTNoneDenormalized for quick access

Database Choice

DatabaseProsConsBest For
PostgreSQLACID, mature, indexesVertical scaling limitsModerate scale
MySQLWell-understood, replicationSimilar scaling limitsModerate scale
DynamoDBAuto-scaling, managedEventually consistent, costlyHigh scale, AWS
CassandraLinear scaling, high writeEventual consistencyVery high scale

Recommendation: Start with PostgreSQL with read replicas, migrate to Cassandra/DynamoDB at scale

Sharding Strategy

Loading diagram...

AWS Alternative: Use Amazon DynamoDB which handles partitioning automatically based on partition key.

Sharding KeyProsCons
short_codeEven distribution, direct lookupsRequires consistent hashing for scaling
user_idGood for user-specific queriesHot users cause hot shards
created_atGood for time-based cleanupRecent shards are hot

Step 6: Caching Strategy

Cache Architecture

Loading diagram...

Cache Configuration

Cache TierTTLSizeHit Rate
CDN24 hoursUnlimited60-70%
Local memory5 minutes10K URLs20-30%
Redis30 days100M URLs95%+

URL access follows Zipf distribution - a small percentage of URLs get most traffic.

Cache Invalidation

StrategyWhen to Use
TTL-basedDefault for all entries
Write-throughUpdate cache on every write
Delete on expireWhen URL expires or is deleted

Step 7: Analytics (Optional)

Real-Time Click Tracking

Loading diagram...

Tracked Metrics

MetricStorageUse Case
Total clicksCounter in main DBQuick access
Clicks over timeTime-series DBGraphs
Geographic dataAnalytics DBCountry/city breakdown
ReferrerAnalytics DBTraffic sources
Device/browserAnalytics DBUser agent analysis

Step 8: Security Considerations

Preventing Abuse

ThreatMitigation
Enumeration attacksRandom codes, not sequential
Malicious URLsURL scanning, blocklist checking
Spam creationRate limiting, CAPTCHA, authentication
URL hijackingOwnership verification for custom aliases

Rate Limiting

OperationLimitScope
URL creation100/hourPer user/IP
Redirects1000/minutePer short code
Custom alias10/dayPer user

Real-World Systems

ServiceScaleNotable Features
bit.ly600M clicks/dayEnterprise analytics, branded links
TinyURLOriginal serviceSimple, no account needed
RebrandlyFocused on brandingCustom domains, link management
short.ioAPI-firstWhite-label solution
T.co (Twitter)Billions/dayIntegrated malware scanning

Summary: Key Design Decisions

DecisionOptionsRecommendation
Code generationHash, Counter, Random, SnowflakeSnowflake ID for uniqueness + time ordering
Code length6-8 characters7 characters (3.5T combinations)
DatabaseSQL, NoSQLPostgreSQL to DynamoDB at scale
CachingRedis, MemcachedRedis Cluster with 30-day TTL
Redirect type301 vs 302301 for performance, 302 for analytics
AnalyticsSync vs AsyncAsync via message queue