Skip to content

URL Shortener

beginner · 30 min · Asked often · Last reviewed 31 Oct 2026 · by TheLearningLoop Team

Key takeaways

  • A URL shortener is read-heavy, so the design is a unique key, a cache in front of a database, and nothing slow in the redirect path.
  • Counter plus base62 never collides. Hashing is stateless but needs collision handling.
  • 301 lets browsers cache the redirect, 302 keeps every visit visible to you for analytics and changes.

Why a URL shortener is a classic design question, and what it teaches

A URL shortener turns a long link into a short one such as sho.rt/2B and redirects visitors who open it. It is small enough to design in an interview and covers the usual ideas: unique IDs, a read-heavy workload, caching, storage and scale.

It gives you two things interviewers always ask about:

  • Scalability: reads (redirects) outnumber writes (new links) by a large factor, so the design is about serving reads cheaply.
  • Availability: a dead redirect breaks every page that links to it, so redirects must keep working when a part fails.

Requirements to confirm first:

  • Functional: create a short link for a long URL, redirect from the short link, optionally a custom alias, expiry and click counts.
  • Non-functional: very low redirect latency, high availability, short codes that cannot be guessed easily or that never collide.

A rough estimate (illustrative numbers, not measurements):

QuantityAssumption or calculationResult
New links per dayAssumed100 million
Writes per second100,000,000 / 86,400 secondsabout 1,160
Reads per second100 reads per write, so 100 x 1,157about 116,000
Links per year100 million x 36536.5 billion
Storage per year36.5 billion x 500 bytes (assumed row size)about 18 TB
Codes needed in 5 years36.5 billion x 5182.5 billion, well under the 3.5 trillion that 7 base62 characters give
Cache size20 percent of one day's new links: 20 million x 500 bytesabout 10 GB

Writes are modest, so most of the design effort goes into serving about 116,000 reads per second.

Skip the design, or simplify it, when:

  • You only need a few links: a table and one server are enough.
  • Links never need to be unique across many writers: a single database sequence is simplest.

Try it

Shorten five links, then read them back. Switch how the short code is made, then break something.

URL Shortener

Step 1 of 11 · Press Space to play, arrow keys to step

Empty system

APIstatelessID counterlast code: -Database0 linksCache0 cached

Initial state. Writes create short codes, reads redirect them. The database is the source of truth, the cache holds hot links.

tick:
0
collisions:
0
cache hits:
0
cache misses:
0
failed reads:
0

1 tick = one step of the simulation.

Things to try:

  • Run Counter + base62 and watch each code come from the next number, so no two links share a code.
  • Switch to Hash of the URL. The code space here has only 4 values to make a collision visible, which is two different URLs getting the same code. In this simulation a colliding URL is simply skipped, so only two of the five links are saved; a real service would retry with a salt until it finds a free code.
  • Choose Database is down for reads. The cache is filled only by reads, and none has happened yet, so all four redirects fail (4 failed reads, 0 hits). A cache protects only links that were read before the outage. The summary caption describes a system that has warmed up; it does not happen in this short run.
  • Then try Cache is empty. Every read goes to the database, repeats included (0 hits, 4 misses), because the cache is held empty for the whole run. Compare None, where the second and third reads of the first link hit the cache (2 hits, 2 misses).

Ways to create the short code

ApproachHow it worksGood whenWeakness
Counter + base62A global counter gives the next number, encoded in letters and digitsYou want unique codes with no collisionsThe counter is one more component, and sequential codes are guessable
Hash of the URLHash the long URL and keep the first charactersThe same URL should give the same codeTruncation causes collisions, which need a retry
Random codePick random characters and check that the code is unusedCodes must not be guessableNeeds a check against the database on every write
Pre-generated keysA separate service makes unused keys in advance and hands them outVery high write rateAnother service to run, and keys must be marked used

Seven base62 characters give about 3.5 trillion codes (62 to the power 7), which is plenty for most services.

Redirect type: 301 vs 302

  • 301 (permanent): the browser caches the redirect, so later visits skip your servers. That cuts load but you stop seeing clicks, and you cannot change the target.
  • 302 (temporary): every visit comes to you. You can count clicks and change or expire the link, at the cost of more traffic.

How the read path works

Most of the work is a redirect, which is a key lookup:

  1. The request GET /2B reaches a stateless web server through a load balancer.
  2. The server looks in the cache for the code. A hit returns the long URL straight away.
  3. On a miss it reads the database by primary key, stores the result in the cache with an expiry, and redirects.
  4. A click event goes to a queue, so counting does not slow the redirect.

Because links are read far more than they are written, the cache can hold only the hot ones and still absorb most traffic.

URL shortener vs the alternatives

ConcernPurpose-built shortenerLong URLs in pagesA single table, no cache
Short, shareable linksYesNoYes
Change the target laterYesNoYes
Handles heavy read trafficYes, with a cacheNot applicableOnly at small scale
Click analyticsYesNoYes, but slow
Extra moving partsCache, ID service, queueNoneFew
Best forPublic links at scaleInternal useA prototype

Build the cache and ID service when traffic grows. A single table is enough for a prototype.

Avoiding a new single point of failure

  • ID counter: give each web server its own range of numbers ahead of time, so a counter outage does not stop writes.
  • Cache: run a replicated cache and let the service fall back to the database if it is down.
  • Database: use replicas for reads and a standby for failover. Partition by code when one machine is too small.

You accept eventual consistency on replicas: a link created a moment ago may take a short while to be readable everywhere.

More failure cases to mention:

  • Read after write: the creator often opens the new link at once. If a replica has not received it yet, the visitor sees a 404, so retry a miss against the primary before giving up, and do not cache a 404 for long.
  • Hot key: one very popular link can overload a single cache node. Keep a short-lived copy in each web server's memory, or replicate that key.
  • Cache stampede: when a popular entry expires, many requests miss at once. Let one request refill the cache and make the others wait.
  • Cache expiry and link expiry: a cache entry must never outlive the link's own expiry time, or an expired link keeps redirecting.

Common mistakes

  • Using the hash alone. Collisions happen. Detect them and retry.
  • Sequential codes for private links. They can be guessed. Use random or shuffled codes if links are secret.
  • Counting clicks in the redirect path. Send events to a queue instead.
  • No expiry or cleanup. Dead links fill the database for ever.
  • Not validating the target. Block unsafe or looping URLs.
  • One huge counter row. It becomes a hotspot. Hand out ranges.
  • Partitioning by code range. Counter codes only increase, so every new write lands on the last partition. Partition by a hash of the code instead.

How to talk about a URL shortener in an interview

  • Start with requirements and a rough estimate: new links per day, a read to write ratio of about 100 to 1, and storage per link. With the illustrative numbers above that is about 1,160 writes and 116,000 reads per second, and about 18 TB a year.
  • Choose the code scheme (counter plus base62 or random) and say why.
  • Draw load balancer, stateless servers, cache and database, and say the read path out loud.
  • Mention 301 vs 302, a queue for analytics, expiry and abuse checks. Name the trade-off: a cache makes reads fast but can serve a link that was just removed until it expires.

Where you meet URL shorteners in real systems

Every social post, SMS and QR code with a short link relies on this design. Here are three drawn out, the first with configuration you can run, then more examples.

Example 1: Storing links and caching hot ones

The database is the source of truth, with the short code as the primary key. The cache holds hot links with an expiry. Open the example below to see the picture and the commands.

Config: link table and cache commandsSetup 1 table and 6 commandsCode no code changes
GET /2BarrivesCache lookuphit returnsDatabase readonly on a miss302 redirectto the long URL
The server checks the cache first. On a miss it reads the database, stores the answer in the cache and redirects.
-- The code is the primary key, so a duplicate code is rejected and a lookup by code is one index read.
CREATE TABLE links (
  code       text PRIMARY KEY,
  long_url   text NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  expires_at timestamptz            -- NULL means the link never expires
);

-- Create a link. If the code already exists the insert fails, which is how a collision is detected.
INSERT INTO links (code, long_url) VALUES ('2B', 'https://example.com/some/very/long/path');

-- Look up a link, ignoring expired ones.
SELECT long_url FROM links
WHERE code = '2B' AND (expires_at IS NULL OR expires_at > now());

The Redis commands were run against a real Redis 7 server: the counter, a cached link with an expiry, a second SET ... NX that did not overwrite, and the click counter. The SQL was run against a real PostgreSQL 16 server. Names and sizes are examples.

Example 2: Reads dominate writes

A popular link can be opened millions of times after one write. Most requests are answered by the cache, and only misses reach the database.

VisitorsLoad balancerServersCacheDatabase
Visitors reach stateless servers through a load balancer. The cache answers most reads, and the database is asked only on a miss.

Example 3: Handing out ID ranges

If every write needs a central counter, the counter is a bottleneck and a single point of failure. Each server asks for a block of numbers once, then uses them locally. The coordinator must save the highest block it has handed out (for example in one database row updated atomically), or a restart could issue the same block twice. If a server crashes, its unused numbers are skipped, which leaves gaps but never duplicates.

Server 1123…Server 210011002…Server 320012002…
Each server owns a block of numbers and encodes them itself. The central counter is contacted only when a block runs out.

More real-world scenarios

The scenarios below are illustrative descriptions of common uses, not claims about any named company.

4. Social media posts

Long tracking links are replaced by short ones so a post stays within its character limit and clicks can be measured.

5. SMS and email campaigns

Short links cut the length of a message, and a unique code per recipient tells the sender who clicked.

6. QR codes

A short link makes a simpler, more reliable QR code, and the target can be changed after printing.

7. Expiring links

A document link works for 24 hours. The expiry time is stored with the link, and a clean-up job deletes old rows. The cache expiry must not be longer than the time the link has left, or an expired link keeps redirecting from the cache.

8. Custom aliases

A user asks for sho.rt/summer. The primary key rejects it if taken, and the service returns a clear error.

9. Abuse and malware

Services check targets against known-bad lists and remove links reported as harmful, so the redirect must respect a disabled flag.

10. Click analytics

Each redirect emits an event to a queue. A consumer groups events into counts by day, country and device.

Tools and managed services

KindNameNotes
CacheRedis, MemcachedHold hot links and the counter
Relational databasePostgreSQL, MySQLPrimary key lookup by code
Key-value storeAmazon DynamoDB, Apache CassandraScale by partitioning on the code
QueueApache Kafka, RabbitMQCarry click events
Load balancerNGINX, HAProxySpread redirects across servers

The takeaway

Whenever a design is a read-heavy key lookup, the shape is the same: a unique key, a cache in front of a database, and no hard work in the read path.

Interview questions

Try answering out loud before you reveal hints. Interviewers care about how you reason, not just the final answer.

  1. 1.

    How would you generate the short code, and what are the trade-offs?

    medium
  2. 2.

    The service gets about 100 redirects for every new link. How does that shape the design?

    easy
  3. 3.

    Should the redirect use HTTP 301 or 302?

    easy
  4. 4.

    The database goes down. What still works, and how do you improve that?

    medium
  5. 5.

    How do you stop the ID counter becoming a bottleneck or single point of failure?

    hard
  6. 6.

    Give a rough capacity estimate for a service that creates 100 million links a day.

    medium
  7. 7.

    A user creates a link and opens it at once, but gets a 404. Why, and what do you do?

    hard

Finished this lesson?

Progress is saved on this device. Create a free account to keep it everywhere.

Topics:Url shortenerTinyurlBase62CachingCase studyRead heavy

↑ Back to top

Next lesson