
Durgesh Tiwari
Author
A URL Shortener is a service that converts a long URL into a short, simple, and easy-to-share link.
For example:
Long URL:
<https://learncodewithdurgesh.com/tutorials/system-design>
Short URL:
<https://learncodewithdurgesh.com/s/sysdesign>When a user opens the short URL, the system finds the original URL and redirects the user to the correct page.
At a basic level, the flow looks simple:
Long URL
↓
Generate Short Code
↓
Store URL Mapping
↓
Return Short URL
↓
Redirect UserThe real challenge starts when the system needs to handle millions of users, billions of short links, and very high redirect traffic.
At that scale, we need to solve important system design problems such as:
How do we generate billions of unique short codes?
How do we make URL redirects very fast?
How do we handle a viral short URL with millions of requests?
Where should we store short URL mappings?
How do we reduce database load?
How do we scale the application and database?
How do we track click analytics without slowing down redirects?
In this URL Shortener System Design, we will design a scalable service like Bitly step by step. We will start with the basic requirements, build a simple architecture, and then improve the design for high traffic, low latency, caching, database scaling, analytics, and reliability.
A URL Shortener is a service that converts a long URL into a short, simple, and easy-to-share link.
For example:
<https://learncodewithdurgesh.com/tutorials/system-design>
↓
<https://learncodewithdurgesh.com/s/sysdesign>Here, the short URL acts as a reference to the original URL. It does not replace or change the original web address.
When a user opens the short link, the URL Shortener finds the original URL and redirects the user to the correct page.

The basic URL creation flow is:
Long URL
↓
URL Shortener
↓
Generate Short Code
↓
Store URL Mapping
↓
Return Short URLThe system may store a mapping like this:
sysdesign → <https://learncodewithdurgesh.com/tutorials/system-design>Later, when a user opens the short URL, the redirect flow looks like this:
User Opens Short URL
↓
URL Shortener
↓
Find Original URL
↓
Redirect User
↓
Original WebsiteCreating one short URL is simple. The real system design challenge starts when the service needs to handle millions of users, billions of URL mappings, and a very large number of redirect requests.

Before designing the URL Shortener architecture, we should first understand what the system needs to do and how users will use it.
This helps us make better decisions about APIs, storage, caching, and scaling.
Our URL Shortener should support these main features:
Create a short URL from a long URL.
Redirect the short URL to the original URL.
Support custom aliases when needed.
Support link expiration.
Track basic click analytics.
For example, a user can create a short URL using:
POST /api/v1/urlsThe basic creation flow is:
Long URL
↓
Generate Short Code
↓
Store URL Mapping
↓
Return Short URLExample:
<https://www.easybuy.com/products/12345>
↓
<https://short.ly/aZ9kLm>When someone opens the short URL:
GET /aZ9kLmthe service finds the original URL and redirects the user to the correct website.
The system should also work well under heavy traffic.
Important requirements include:
Low latency — redirects should happen very quickly.
High availability — users should be able to open short links even if one server fails.
Scalability — the system should handle growing traffic and billions of stored URLs.
Unique short codes — two different URL mappings should not accidentally use the same code.
Durability — stored URL mappings should remain safe even after server failures or restarts.
Fast reads — redirect requests should stay fast because they happen much more often than URL creation.
A URL Shortener is usually a read-heavy system.
A user may create a short URL only once, but that same link can be opened thousands or even millions of times.
For example:
Create Short URL
↓
1 Write Request
Short URL Shared
↓
Thousands or Millions of Redirect RequestsSo in most cases:
Redirect Requests >>> URL Creation RequestsThis is important because it tells us where to focus our design.
We should optimize the redirect path first. That means using fast lookups, caching frequently used URLs, and reducing unnecessary database requests.
These traffic patterns will directly influence our Redis caching, database design, and scaling strategy.
In a system design interview, we do not need exact numbers. We only need rough estimates to understand how much traffic and data the system may handle.
Let us assume:
10 million new URLs per day
100 million redirect requests per day
This gives us roughly:
Read : Write Ratio
10 : 1This means the system receives far more redirect requests than URL creation requests.
Now imagine that we keep URL mappings for several years. Over time, the system can store billions of records.
This tells us a few important things:
We need reliable persistent storage for URL mappings.
We need fast lookup using the short code.
We should avoid querying the database for every redirect.
We should use caching to handle frequent reads efficiently.
Suppose we use Base62 for short-code generation.
Base62 uses:
0-9 → 10 characters
a-z → 26 characters
A-Z → 26 characters
Total = 62 charactersIf we use a 7-character short code:
62^7 ≈ 3.5 trillion combinationsThat gives us a very large number of possible short codes.
For example:
aZ9kLm2
X7pQ91a
b2Kx8MnSo a 7-character Base62 code is enough for a very large-scale URL Shortener in many practical designs.
Interview point: Capacity estimation helps us understand whether the system needs caching, distributed storage, sharding, or other scaling techniques. We should estimate first and add complexity only when the expected scale actually requires it.
A URL Shortener does not need many APIs. We only need a few core endpoints to create, open, delete, and track short URLs.
The client sends a long URL, and the system returns a short URL.
POST /api/v1/urlsExample request:
{
"longUrl": "<https://www.easybuy.com/products/12345>"
}Example response:
{
"shortUrl": "<https://short.ly/aZ9kLm>"
}The service validates the long URL, generates a unique short code, stores the mapping, and returns the short link.
When a user opens a short URL, the service finds the original URL and redirects the user.
GET /aZ9kLmThe basic flow is:
Short URL
↓
Find short code
↓
Get original URL
↓
Redirect userThis API should be very fast because redirect requests happen much more often than URL creation.
A user can delete or disable a short URL using:
DELETE /api/v1/urls/aZ9kLmAfter deletion, the short URL should no longer redirect to the original page.
Users may also want to check basic statistics for a short URL.
GET /api/v1/urls/aZ9kLm/analyticsThe response can include data such as total clicks, clicks by date, device type, country, or referrer.
We should keep analytics processing separate from the main redirect path. The user should reach the destination quickly without waiting for click data to be stored or processed.
Let us start with a simple URL Shortener architecture and then improve it as traffic grows.
User
↓
Load Balancer
↓
URL Service
/ \
↓ ↓
Redis Cache Database
│
↓
Event Queue
↓
Analytics ServiceEach component has a clear role:
Load Balancer — Distributes incoming requests across multiple URL Service instances.
URL Service — Handles short URL creation, redirects, deletion, and other application logic.
Redis Cache — Stores frequently accessed short_code → long_url mappings for faster redirects.
Database — Stores permanent URL mappings and related metadata.
Event Queue — Sends click events for background processing without slowing down redirects.
Analytics Service — Processes click events and stores analytics data.
This design keeps the main URL redirect flow fast and simple. As traffic grows, we can scale each component independently.
The most important data in a URL Shortener is the mapping between a short code and the original URL.
A simple URL record can contain:
Field | Purpose |
|---|---|
| Stores the unique short code |
| Stores the original URL |
| Identifies the link owner, if required |
| Stores when the short URL was created |
| Stores when the link should expire |
| Shows whether the link is active, disabled, or deleted |
For example:
aZ9kLm
↓
<https://www.easybuy.com/products/12345>The most common lookup is:
short_code → long_urlSo short_code should have a unique index.
This gives us two benefits:
It prevents duplicate short codes.
It makes redirect lookups faster.
Both SQL and NoSQL databases can work for a URL Shortener.
A relational database such as PostgreSQL or MySQL is a good starting choice because it provides strong constraints, indexes, and simple data management.
A distributed key-value or NoSQL database can become useful when the system grows to a very large scale and needs massive distributed reads and writes.
For the initial design, I would start with a relational database and create a unique index on short_code.
We should not add database sharding too early. A properly indexed database, Redis cache, and replication can handle a large amount of traffic before sharding becomes necessary.
Interview point: Start with the simplest database that meets the current scale. Add sharding only when storage size or database throughput becomes a real bottleneck.
Short-code generation is one of the most important parts of a URL Shortener System Design interview. Every short URL needs a unique code so that it always points to the correct original URL.
There are three common approaches.
We can generate a random combination of letters and numbers.
For example:
aZ9kLm
Qp7X2aEasy to generate and difficult to predict.
Works well when we have a large code space.
Two requests may generate the same code, so we need to detect and handle collisions.
We can create a hash from the original URL and use part of that hash as the short code.
For example:
Long URL
↓
Hash Function
↓
Full Hash
↓
Take Few Characters
↓
aZ9kLmThe same input can produce the same hash.
Using only part of the hash keeps the code short.
Different URLs may still produce the same shortened code, so collision handling is required.
We can first generate a unique numeric ID:
125839Then encode that ID using Base62:
125839 → X7pLBase62 uses:
0-9
a-z
A-ZThis gives us 62 possible characters and creates short, URL-friendly codes.
At a large scale, multiple servers may generate short URLs at the same time. Instead of depending on one local counter, we can use a distributed ID generator to generate unique IDs across servers.
Approach | Main Benefit | Main Concern |
|---|---|---|
Random String | Harder to predict | Collision handling |
Hash | Simple to generate from URL | Possible collisions |
Unique ID + Base62 | Easy to guarantee uniqueness | IDs may be predictable |
For our URL Shortener, Unique ID + Base62 is a good choice when simple and reliable uniqueness is the main requirement.
If we also want short codes that are difficult to guess, we can use a random-code approach instead.
Redirects are the most frequent operation in a URL Shortener, so this flow should stay fast and lightweight.
Suppose a user opens:
<https://short.ly/aZ9kLm>The service extracts the short code:
aZ9kLmThen it looks for the original URL.
User
↓
GET /aZ9kLm
↓
URL Service
↓
Check Redis
/ \
Hit Miss
↓ ↓
URL Database
↓ ↓
Redirect Update Cache
↓
RedirectA cache hit happens when Redis already contains the short-code mapping.
For example:
aZ9kLm → <https://www.easybuy.com/products/12345>In this case:
The URL Service gets the original URL directly from Redis.
It does not need to query the database.
The redirect becomes faster.
A cache miss happens when Redis does not contain the required mapping.
In that case:
The URL Service checks the database.
It gets the original URL.
It stores the mapping in Redis.
It redirects the user.
The next request for the same short URL can then use the cached value.

Another common URL Shortener interview question is whether we should use a 301 or 302 redirect.
301 Redirect | 302 Redirect |
|---|---|
Permanent redirect | Temporary redirect |
Browsers may cache it more strongly | Requests are more likely to return to our service |
Can reduce repeated server traffic | Better for click tracking |
Useful when the destination will not change | Useful when analytics or destination changes matter |
For a URL Shortener that needs click analytics, a 302 redirect is often easier to manage because requests continue to reach our service.
If reducing repeat traffic is more important and the destination will remain fixed, a 301 redirect may be a better choice.
Interview point: Choose
301or302based on product requirements. Use302when tracking and control are more important, and consider301when permanent caching is useful.

Caching is very important in a URL Shortener because redirect requests happen much more often than new URL creation.
If every redirect request goes directly to the database:
100 Million Redirect Requests
↓
Databasethe database can become a bottleneck.
To reduce this load, we can use Redis Cache.
Request
↓
Redis
/ \
Hit Miss
↓ ↓
URL Database
↓
Update RedisCache Hit — Redis already has the URL mapping, so the service can redirect the user quickly.
Cache Miss — Redis does not have the mapping, so the service reads it from the database and stores it in Redis for future requests.
We can also use a TTL (Time to Live) for cached entries.
For example:
TTL = 24 hoursAfter the TTL expires, Redis removes the cached value. The next request reads the latest mapping from the database and caches it again.
If a short URL is updated, deleted, or expired, we should also update or remove its cached value. This prevents users from getting old or invalid data.
A hot URL is a short link that suddenly receives a very large number of requests.
For example, if a celebrity or popular company shares one short URL, millions of users may open it within a short time.
We should not allow every request to reach the database.
Instead:
Keep the popular URL in Redis.
Use multiple application servers to handle traffic.
Use local cache when needed.
Use CDN or edge caching for very high global traffic.
A simple flow can look like:
Users
↓
CDN / Edge
↓
URL Service
↓
Redis
↓
DatabaseThe main goal is simple: serve repeated requests from faster cache layers and use the database only when needed.
This reduces redirect latency and protects the database from unnecessary load.
Interview point: URL Shorteners are read-heavy systems, so caching helps improve redirect speed and reduce database traffic, especially for popular or viral links.
As traffic grows, one application server may not be enough. We need to scale the system so it can handle more users and more redirect requests without slowing down.
We can place a Load Balancer in front of multiple URL Service instances.
Load Balancer
/ | \
↓ ↓ ↓
Server 1 Server 2 Server 3The Load Balancer sends incoming requests to healthy servers.
This helps us:
Distribute traffic across multiple servers.
Avoid overloading one server.
Keep the service available if one server fails.
We should keep the URL Service stateless.
This means the application servers should not store important URL or user data only in local memory.
Instead, shared data should stay in systems such as:
Redis
Database
Shared storage when required
Because the servers are stateless, we can add more instances when traffic increases.
3 Servers
↓
5 Servers
↓
10 ServersThis is called horizontal scaling.
A URL Shortener receives many read requests, especially redirects.
We can use read replicas to reduce load on the main database.
Primary Database
/ \
↓ ↓
Read Replica 1 Read Replica 2The primary database handles writes, while read replicas can handle suitable read requests.
This improves read capacity and also helps with availability.
As the number of stored URLs grows, one database may eventually become too large or too busy.
At that point, we can split the data across multiple database servers. This is called sharding.
For example:
Shard 1 → Part of URL mappings
Shard 2 → Part of URL mappings
Shard 3 → Part of URL mappings
Shard 4 → Part of URL mappingsOne simple approach is:
hash(short_code) % number_of_shardsThis helps decide which shard should store a particular URL mapping.
Sharding improves storage and traffic distribution, but it also adds complexity. We should introduce it only when a single database can no longer handle the required scale.
A viral URL may receive millions of requests in a short time.
We should avoid sending every request to the database.
Instead, we can serve popular URLs from faster layers.
Users
↓
CDN / Edge Cache
↓
URL Service
↓
Redis
↓
DatabaseFor very popular links:
CDN or edge cache can serve users closer to their location.
Redis can handle repeated URL lookups quickly.
Multiple URL Service instances can share the traffic.
The database should only handle requests that cannot be served from faster layers.
The main goal is to scale the read path first, because redirects generate much more traffic than URL creation.
Users may want to understand how their short links are performing.
Useful analytics can include:
Total clicks
Click time
Country
Device type
Referrer
However, analytics should not slow down the redirect flow.
A poor design would save analytics before redirecting the user:
Redirect Request
↓
Save Analytics
↓
Wait
↓
Redirect UserThis adds extra work to the user-facing request. If the analytics database becomes slow, the redirect can also become slow.
A better approach is to process analytics asynchronously.
Redirect User
│
└────→ Publish Click Event
↓
Kafka / Queue
↓
Analytics Consumer
↓
Analytics StoreThe URL Service redirects the user immediately and sends the click event to Kafka or another queue.
The analytics consumer processes the event separately and stores the required information.
This keeps the redirect path fast and allows the analytics system to scale independently.

Interview point: Keep analytics outside the critical redirect path. Use asynchronous event processing so click tracking does not increase redirect latency.
A public URL Shortener can attract abuse because users can hide the real destination behind a short link.
Attackers may try to:
Create large numbers of short URLs
Send spam links
Hide phishing websites
Redirect users to malicious pages
Abuse public APIs
We should protect the service with a few important security controls.
Rate Limiting — Limits how many short URLs a user or IP address can create in a given time.
URL Validation — Checks whether the submitted URL has a valid format and uses an allowed protocol such as http or https.
Malicious URL Detection — Blocks known phishing, malware, or unsafe destinations.
Authentication and Authorization — Protects private operations such as deleting links or viewing analytics.
HTTPS — Protects communication between users and the URL Shortener.
For example, the system may limit URL creation like this:
100 URL creation requests
per user
per minuteIf a user crosses the limit, the API can reject additional requests for a short period.
The main goal is to protect the most exposed parts of the system without making the first design unnecessarily complex.
A good URL Shortener should handle unexpected situations without sending users to the wrong destination.
This is called a collision.
If the generated short code already exists, the system should generate a new one and try again.
A unique database constraint on short_code can also help prevent duplicate mappings.
Suppose a user wants:
short.ly/saleIf another user already owns sale, the system should reject the request and ask the user to choose a different alias.
A suitable response can be:
409 ConflictIf the current time is greater than the link's expires_at value, the system should stop redirecting users.
It can return an expired-link response and remove any stale cached value.
Short URL
↓
Check Expiration
↓
Expired?
├── No → Redirect
└── Yes → Return Expired ResponseIf the service cannot find the short code in Redis or the database, it should return a not-found response.
404 Not FoundThe system should never redirect the user to an unrelated URL.
Interview point: Important edge cases include short-code collisions, duplicate custom aliases, expired links, and invalid short codes. Handling them correctly keeps the redirect system reliable.
In a system design interview, choosing a technology is not enough. Interviewers also want to understand why you chose it and what trade-off comes with that decision.
Decision | Option 1 | Option 2 | Main Trade-Off |
|---|---|---|---|
Redirect | 301 | 302 | Lower repeat traffic vs better tracking and control |
Short Code | Base62 ID | Random Code | Easy uniqueness vs harder-to-guess links |
Database | SQL | NoSQL / Key-Value | Strong relational features vs distributed scalability |
Analytics | Synchronous | Asynchronous | Immediate processing vs faster redirects |
Scaling | Vertical | Horizontal | Simpler setup vs better large-scale growth |
Read Path | Database Only | Redis + Database | Simpler design vs lower latency and less database load |
For our URL Shortener, the right choice depends on the system requirement.
Simply saying:
“I will use Redis.”
is not enough in an interview.
A better answer is:
“Redirect requests are much higher than URL creation requests. If every redirect hits the database, the database can become a bottleneck. I would use Redis to cache frequently accessed URL mappings, reduce database load, and improve redirect latency.”
The same thinking applies to every major design decision:
Use 302 when click tracking and destination control matter.
Use Base62 with unique IDs when simple and reliable uniqueness is important.
Start with SQL when the scale is manageable and move to distributed storage only when needed.
Use asynchronous analytics so click tracking does not slow down redirects.
Use horizontal scaling when traffic grows beyond one server.
Use Redis when repeated reads would otherwise overload the database.
Now we can combine the main components into one complete URL Shortener architecture.
Users
↓
CDN / Edge
↓
Load Balancer
↓
┌────────────┴────────────┐
↓ ↓
URL Service 1 URL Service 2
│ │
└────────────┬────────────┘
↓
Redis Cache
↓
Database
/ \
↓ ↓
Read Replica Shards
│
URL Services ──────────────┴──→ Event Queue
↓
Analytics Service
↓
Analytics StoreThis architecture separates the main responsibilities:
CDN / Edge — Handles popular requests closer to users when needed.
Load Balancer — Distributes traffic across URL Service instances.
URL Service — Creates short URLs and handles redirects.
Redis Cache — Provides fast access to frequently used URL mappings.
Database — Stores URL mappings permanently.
Read Replicas / Shards — Help the database handle larger traffic and data.
Event Queue — Sends click events for asynchronous processing.
Analytics Service — Processes and stores click analytics.
When a user creates a new short URL:
Client
↓
POST Long URL
↓
URL Service
↓
Generate Unique Short Code
↓
Store URL Mapping
↓
Return Short URLThe URL Service generates a unique short code, stores the mapping in the database, and returns the short URL to the client.
When someone opens an existing short URL:
User Opens Short URL
↓
Load Balancer
↓
URL Service
↓
Check Redis
/ \
Hit Miss
↓ ↓
Redirect Database
↓
Update Cache
↓
RedirectRedis handles frequently accessed URLs quickly. The database is used when the required mapping is not available in the cache.
Click analytics should run separately from the main redirect flow.
Redirect Request
↓
Publish Click Event
↓
Event Queue
↓
Analytics Consumer
↓
Analytics StoreThis allows the system to collect click data without making users wait for analytics processing.
A short URL is usually created once but can be opened many times. Therefore, redirect requests are much higher than URL creation requests.
We can generate unique IDs and encode them using Base62. At distributed scale, we can use a distributed ID generator. Random codes are another option when unpredictability matters.
Base62 uses numbers, lowercase letters, and uppercase letters. It can represent large numeric IDs using relatively short, URL-friendly strings.
Redis reduces redirect latency and database traffic by storing frequently accessed URL mappings in memory.
The application can fall back to the database, but database traffic may increase sharply. In production, we would also consider Redis replication or clustering and protect the database from sudden overload.
We should serve the URL from Redis or an edge/CDN cache instead of repeatedly querying the database. We can also scale stateless URL Service instances horizontally.
It depends on the product. A 301 can reduce repeated traffic because clients may cache it. A 302 gives the service more control and makes click tracking easier.
We can start with proper indexing and caching. As traffic grows, we can add read replicas. If storage or write throughput becomes too large for one database, we can introduce sharding.
I would publish click events to Kafka or another message queue and process them asynchronously using analytics consumers.
The common bottlenecks are database reads, hot URLs, cache capacity, application-server traffic, and short-code generation at very large scale. We address them using caching, horizontal scaling, replication, sharding when required, and distributed ID generation.
If an interviewer asks you to design a URL shortener like Bitly, do not start by naming ten technologies.
Start with the requirements.
Then estimate the traffic, define the APIs and data model, and draw a simple architecture. After that, explain how you generate unique short codes and make redirects fast with caching.
Once the basic design works, discuss scaling, database replication or sharding, viral URLs, asynchronous analytics, security, and important trade-offs.
A strong system design answer should show this thinking:
Requirements
↓
Estimate Scale
↓
API + Data Model
↓
Simple Architecture
↓
Find Bottlenecks
↓
Improve the Design
↓
Discuss Trade-OffsThat approach makes the URL Shortener System Design easier to understand and also gives you a clear structure to follow during a real system design interview.