How URL shorteners actually work
A short URL is a database row, an HTTP redirect, and a series of decisions you don't usually see. Here is what happens when you click bit.ly/anything.
A short URL feels like magic — five characters that resolve to a fully-qualified URL ten times their length. The mechanism behind it is unmagical in the most satisfying way: it is one of the smallest possible web applications, three components that together do exactly one thing.
This post walks through what actually happens between the moment you click bit.ly/abc123 and the moment your browser lands on the destination. If you are thinking about building a shortener, or just curious what is going on inside one, this is the model to start from.
The three components
A shortener has three jobs. The whole product, no matter how it markets itself, is a thicker version of these:
Aside
Generate a short code · Store the mapping · Redirect on request. Anything else — analytics, custom domains, QR codes, A/B testing — is a wrapper around these three operations.
1. Code generation
When you submit https://example.com/some/very/long/path?with=querystring, the service has to give you back something like bit.ly/3xY9pQ. The string 3xY9pQ is the short code. Generating short codes well is the most interesting computer-science problem in the system.
Three common approaches:
- Base62 encoding of an auto-incrementing ID. Each new link gets the next integer from a counter (1, 2, 3, ...). That integer is then encoded in base62 (using
0-9,a-z,A-Z, giving 62 characters per position). An ID of1becomes1. An ID of100,000becomesQ0u. An ID of one billion fits in six characters. This is what most services use because it is collision-free by construction — every code maps to exactly one ID, and you can hand them out forever. - Random strings with collision check. Generate a random N-character string, check the database, retry if it exists. Conceptually simpler but at scale, you spend more and more of your time looking up keys that already exist.
- Hash of the long URL, truncated. Take SHA-256 of the destination, take the first six characters. This means the same long URL always produces the same short URL, which is sometimes desirable and sometimes a privacy bug.
The base62-of-counter approach is dominant because it is efficient, predictable, and lets you reason about capacity. Six characters of base62 give you 62⁶ ≈ 56 billion possible codes, which is more than enough for any service that is not Bitly itself.
2. Storage
The mapping 3xY9pQ → https://example.com/some/very/long/path has to live somewhere. The choice of where is mostly about read performance, because shorteners are extremely read-heavy: a successful service might get one create per thousand reads, or worse.
Common choices:
- Redis or another in-memory key-value store for the hot path. Lookups are sub-millisecond and the data structure (string-to-string) is exactly what you need.
- Postgres or another relational database for durable storage, with the short code as a primary key or an indexed column. Reads off a B-tree index are fast enough for most services.
- Both, in a layered cache. Redis in front, Postgres behind it. Cache misses fall through to the database; cache hits respond in microseconds.
What you do not want is a database query that joins multiple tables on every redirect, because the redirect is the critical path: it sits between the user and the page they wanted.
3. The redirect
When the request for bit.ly/3xY9pQ arrives, the server looks up the long URL and sends back an HTTP redirect. There are two flavors and the choice has consequences.
| Status | Meaning | Cacheable | Use when |
|---|---|---|---|
| 301 | Permanent | Yes (often aggressively) | You want the user's browser to remember the destination and skip you next time. |
| 302 | Temporary | Generally no | You want to count every visit, or the destination might change. |
Almost every commercial shortener uses 302. A 301 means the user's browser can cache the redirect and bypass the service entirely on future visits — which is faster for the user but invisible to your analytics. If your business is analytics, 302 is the right answer. If your goal is just shortening for shortening's sake, 301 is friendlier.
Everything else is a wrapper
Once you have those three pieces, the entire feature surface of a service like Bitly is built on top:
- Custom domains (
yourbrand.co/promo) is the same system, but the redirect handler matches onHostheader before looking up the code. The service has to verify you own the domain and provision an SSL certificate (typically via Let's Encrypt). - Branded slugs (
bit.ly/black-friday-2026) is just letting users supply their own short code instead of accepting the auto-generated one. The collision check becomes a validation: is this code already taken? - Click analytics is an event fired during the redirect — the user gets their 302 immediately, and the service async-writes a row to an events table with timestamp, IP-derived geo, user-agent-derived device.
- QR codes are the same long URL, encoded as a 2D barcode. The QR is independent of the shortener; you can encode any URL — short or long — into a QR code with no service involvement at all. (We have a QR generator that does exactly this.)
- Link-in-bio is a separate product entirely: a tiny landing page per user, with multiple links on it. The shortener part is incidental.
What this means if you're building one
If you are tempted to build a URL shortener — and many people are, it is one of the canonical "small full-stack project" ideas — start with these three components and resist adding a fourth until they all work cleanly. A common failure mode is trying to build the analytics dashboard before the redirect handler is fast and reliable.
A few shortcuts that have aged well:
- Use Postgres with an indexed
short_codecolumn. Do not reach for a NoSQL database for this; it doesn't earn its complexity. - Generate codes from
nanoidor your language's equivalent until you outgrow it. Auto-incrementing IDs leak your growth rate to anyone who creates two links a week apart and counts the difference. - Put a CDN in front of the redirect. Cloudflare or Fastly will absorb most of your traffic without ever asking your origin.
- Log redirects to a write-optimized append-only store (Clickhouse, Tinybird, or just a daily Parquet file) — not back into your transactional database.
Further reading
If this post hooked you, the next layer is link-in-bio architecture (a different problem) and the surprisingly deep rabbit hole of QR code error correction, which trades data density for damage tolerance in ways that matter more than you'd think.
Frequently asked
- What is a URL shortener?
- A URL shortener is a service that creates a short, often random-looking URL that redirects to a longer original URL. Examples include bit.ly, t.co, and goo.gl (now retired). The short URL is stored in a database alongside the long one; when someone visits the short URL, the service looks up the mapping and issues an HTTP redirect.
- What is the difference between a 301 and a 302 redirect for short URLs?
- A 301 is a permanent redirect — browsers and search engines may cache it, meaning future visits skip the shortener entirely. A 302 is temporary and forces the browser to ask the shortener every time. Most commercial shorteners use 302 because they want the analytics hit on every click. If you do not need analytics, 301 is faster for the user.
- Are short URLs less secure than regular URLs?
- Short URLs are not inherently less secure, but they hide the destination, which is what attackers exploit. Phishing campaigns frequently use shorteners to obscure malicious links. Most shorteners scan their destinations against blocklists, but the protection is imperfect. As a recipient, you can usually preview a short URL by appending a + (for bit.ly) or using a third-party expander.