Blog 4
DNS to CDN — How the Internet Actually Delivers a Request
A user types a web address and presses Enter. A fraction of a second later, the page appears.
To the user, that felt like one thing.
To an engineer, a dozen systems just cooperated flawlessly across the planet — and most developers have never really looked at what they are.
Parts 1 through 3 gave you the mindset and the method. You can now walk up to an ambiguous problem, think in constraints, and run C.R.E.D to turn it into a defensible design.
But there's a layer beneath all of that which most engineers have genuinely never examined — the path a single request travels before your code ever runs, and the path the response travels back. And here's why it matters: every large system you'll ever design sits on top of this journey. You can't reason about latency, failover, caching, or global performance if the request path is a black box to you.
So this article opens the black box. We're going to follow one request — one URL — all the way through the internet and back, hop by hop, and understand why each layer exists. Not to networking-engineer depth; we won't touch BGP internals or DNS packet formats. Just enough that the boxes in every future architecture diagram stop being abstract and start being systems you can reason about.
Let's follow a request.
One URL, Many Systems
A user types this and presses Enter:
https://www.example.com
To them, a page loads. Done. But in the milliseconds between that keypress and that page, a remarkable amount of coordinated work happens across systems that may span continents: the browser figures out where to even send the request, DNS translates the human-friendly name into a machine address, a CDN edge, possibly on another continent, may answer instantly, a load balancer decides which of many servers should handle it, a reverse proxy shapes, secures, and routes the traffic, an application server finally runs the business logic, a cache tries to answer before the database has to, a database holds the durable source of truth, and observability systems quietly watch all of it.
Nine kinds of systems, cooperating within a heartbeat. The user experiences one seamless moment. The engineer's job is to understand the seams — because that's where performance, reliability, and cost are actually decided.
Let's map the whole journey first, then walk it step by step.
The Big Picture: The Request Journey
Here's the simplified path, top to bottom:
Browser
↓
DNS
↓
CDN Edge
↓
Load Balancer
↓
Reverse Proxy
↓
Application Server
↓
Cache
↓
Database
Hold onto this as your mental model — but with one honest caveat: this is a simplified diagram, and real systems bend it. In practice, layers get combined, reordered, or skipped depending on the design: the CDN may terminate TLS (handle the encryption handshake) right at the edge; the reverse proxy may also be the load balancer — one component, two roles; an API gateway may sit in front of the services, doing routing and auth; the cache may appear at several layers, not just one; the database may be reached through read replicas or connection pools, not directly.
None of that invalidates the model. It just means the clean vertical line above is a teaching diagram, not a literal wiring diagram of every system. The value is understanding what each layer does and why it exists — because once you know that, you can recognize it no matter how a particular architecture arranges it. Let's walk the path.
Step 1: The Browser Understands URLs, But the Network Needs IP Addresses
Here's the first gap you have to cross. Humans think in names. We type www.example.com because it's memorable. But the network underneath doesn't route traffic by names — it routes by IP addresses, numeric labels that identify machines on the internet.
So before the browser can send the request anywhere, it has to translate:
www.example.com → 93.184.216.34
That name is meaningful to a person. That number is meaningful to the network. The browser can't send a request to a name — it needs the address.
The system that performs this translation, billions of times per second across the internet, is DNS. It's the first thing that happens, and until it finishes, nothing else can begin. So that's where our journey really starts.
Step 2: DNS — The Internet's Address Book
DNS — the Domain Name System — is, at its simplest, the internet's address book: you give it a name, it gives you back an address. But how it finds that answer is a small relay race worth understanding, because the details directly affect failover, CDN routing, and global traffic steering — all things a system designer cares about.
Here's the lookup chain when the browser needs to resolve a name:
Browser Cache
↓
OS Cache
↓
Recursive Resolver
↓
Root Nameserver
↓
TLD Nameserver
↓
Authoritative Nameserver
↓
IP address / CNAME response
Walk it hop by hop:
- Browser cache — the browser may have looked this name up recently and still remember the answer. If so, the race is over before it starts.
- OS cache — if the browser doesn't know, the operating system might. Another chance to skip the whole lookup.
- Recursive resolver — if nobody local knows, the request goes to a resolver (usually run by your ISP or a public DNS provider). This is the workhorse: it does the actual legwork of finding the answer on your behalf.
- Root nameserver — the resolver starts at the top. The root doesn't know
example.com, but it knows who's responsible for.comaddresses — it points the way. - TLD nameserver — the
.com"top-level domain" servers know which nameserver is authoritative forexample.com. Another pointer down the chain. - Authoritative nameserver — the final authority for
example.com. It returns the actual answer: an IP address, or a CNAME (an alias pointing to yet another name — which matters a lot for CDNs, as we'll see).
You don't need DNS-engineer depth here. But you do need this chain in your head, because it explains something important: DNS isn't one lookup to one place — it's a distributed, cached, hierarchical system. And because it's cached at multiple levels, the question of how long each level is allowed to remember an answer becomes a real design lever. That lever is TTL.
DNS TTL: Fast Failover vs More DNS Load
Every DNS answer comes with a TTL — Time To Live. It's a simple idea with real consequences: TTL tells every cache along that chain how long it's allowed to reuse this answer before asking again.
And like almost everything in system design (Part 2's lesson returning already), it's a trade-off with no free setting.
Low TTL — answers expire quickly, so caches ask again often:
- faster traffic steering — you can redirect users to new endpoints quickly
- faster failover — if an endpoint dies, the world stops using it sooner
- useful when endpoints or edge health change frequently
- but more DNS queries overall — more load on resolvers and authoritative servers
- and possible extra latency on the first request after a cached answer expires
High TTL — answers are cached for a long time:
- far fewer DNS lookups — better caching, lower overhead
- less load on your DNS infrastructure
- but slower failover — a dead endpoint keeps receiving traffic until caches expire
- and users may keep using stale answers long after you've changed them
This is a genuine engineering decision, not a default to ignore. Planning a migration or a failover strategy? You lower TTLs ahead of time so the world can react quickly when you flip the switch. Running a stable endpoint that rarely changes? A higher TTL saves you needless lookups.
TTL is not just a number. It is a trade-off between control and cache efficiency.
Keep that framing — it's the first of many places in this journey where a knob trades one good thing for another.
Step 3: How DNS Sends Users Toward a CDN
Here's a twist most engineers don't realize until they look closely: for large sites, the domain often doesn't resolve directly to the origin application server at all. Instead, it resolves toward a CDN — a content delivery network with servers spread around the world.
How? Usually through that CNAME alias we mentioned. The chain looks like this:
www.example.com
↓ (CNAME alias)
www.example.cdnprovider.net
↓
CDN edge IP
The site's name points at the CDN's name, and the CDN's DNS decides which specific edge server's IP to hand back. This indirection is exactly why CDNs can move users between servers freely — you published one stable name, and the CDN steers the address behind it.
Now here's the nuance that trips people up. The edge you're sent to is not always the geographically closest one. It feels intuitive that a user in one city should hit the server in that city — but the CDN's mapping logic weighs several factors: the resolver's or user's location, edge health (a nearby edge that's struggling won't be chosen), capacity (a nearby edge that's full may hand you off), the actual network path (closest by map isn't always closest by network), and provider policy and regional availability.
The goal isn't "nearest on a map." It's "the best healthy edge for this user right now." That distinction — nearest-healthy, not nearest-geographic — is a small idea with big implications for how global systems behave, and it's worth internalizing early.
DNS-Based Steering vs Anycast
There are two common ways a user actually reaches a CDN edge, and a system designer should know both exist — because they behave differently during failures.
DNS-Based Steering
The CDN's DNS returns a specific IP for a suitable edge, chosen by the mapping logic above. Different users, asking from different places, get different answers.
Good for: policy-based routing (send these users here, those users there), geo-routing, health-based steering (route away from unhealthy edges), and controlled, deliberate failover.
The catch is the one we already met: because it's DNS, changes are bounded by TTL — a steering decision only takes full effect once cached answers expire.
Anycast Routing
Anycast takes the opposite approach: the same IP address is announced from many locations at once, and the internet's own routing delivers each user to a nearby or best-path edge automatically. Nobody hands out different addresses — everyone gets the same one, and the network sorts out where it goes.
Good for: a simple client experience (one address, everywhere), fast routing to a nearby edge without a per-user DNS decision, and resilience — if an edge stops announcing that address (because it died), the network naturally reroutes to the next-nearest, with no DNS change and no TTL wait.
In practice, modern CDNs use DNS-based steering, Anycast, or a combination of both — steering for deliberate control, Anycast for fast, resilient edge proximity. We won't go into the routing-protocol machinery (BGP) that makes Anycast work here; there's a dedicated bonus later in the series on DNS, Anycast, and global traffic routing that opens that box properly. For now, just hold the two mechanisms in mind: one hands out different addresses, the other routes the same address intelligently.
Step 4: CDN — Serving Content Close to Users
So what is this CDN the user just reached? The simplest mental model is a chain of warehouses.
Imagine a company with one central warehouse. Every order, from every country, ships from that one building — slow for distant customers, and a strain on the single warehouse. Now imagine they place smaller warehouses near their customers, each stocked with the popular items. Most orders now ship from just down the road. That's a CDN: it stores copies of content close to users, all around the world, so requests don't have to travel back to the origin every time.
What a CDN commonly serves: images, videos, and downloads; CSS and JavaScript; static assets generally; and sometimes cached API responses, where it's safe to do so.
When the content is already at the edge — a cache hit — the journey is beautifully short:
User
↓
CDN Edge
↓
Cached content returned ✅ fast, origin never touched
When it isn't — a cache miss — the edge fetches from the origin, keeps a copy, and serves it:
User
↓
CDN Edge
↓
Origin Server (fetch the real thing)
↓
CDN stores the response (for next time)
↓
User receives the response
The first user to request something at a given edge pays the miss cost; everyone after them enjoys the hit. The benefits are substantial: lower latency (content comes from nearby, not across the world), lower origin load (the origin only handles misses, not every request), lower bandwidth cost from the origin (a real money saver at scale), and a better global experience (users everywhere feel local speeds).
But a CDN isn't magic, and the limitations matter: it's excellent for cacheable content, but dynamic, personalized data (your account page, your cart) needs care — you can't just cache "everyone's" version of something that's different per user. And staleness and invalidation are real trade-offs — if content is cached and then changes, how do you make the edges forget the old copy? That's an entire discipline of its own.
Hold that last point, because it leads directly to one of the most misunderstood ideas in system design — that caching isn't one thing in one place.
Step 5: Load Balancer — Spreading Traffic Across Healthy Servers
Suppose the request is a cache miss, or it's dynamic — it has to reach the actual backend. But "the backend" is almost never one server. It's many, and something has to decide which one handles this request. That's the load balancer.
Users
↓
Load Balancer
↓
Server A / Server B / Server C
A load balancer does several jobs at once: it distributes requests across many servers so no single one is overwhelmed, removes unhealthy instances by health-checking servers and steering traffic away from failing ones, enables horizontal scaling (need more capacity? add servers behind the balancer), and improves availability (one server dying doesn't take the system down; traffic just flows to the others).
For this article, think of the load balancer simply as a traffic controller standing in front of your servers, directing each request to a healthy one. How it decides which server — round robin, least connections, weighted routing, sticky sessions, consistent hashing, and the health-check machinery behind it all — is a rich topic with real trade-offs, and it gets its own dedicated deep dive later in the series. For now, the mental model is enough: it spreads traffic across healthy backends, and that alone is what makes horizontal scaling and high availability possible.
Step 6: Reverse Proxy — The Smart Gatekeeper
Sitting in front of the application servers — sometimes as the same component as the load balancer, sometimes separate — is the reverse proxy. If the load balancer is the traffic controller, the reverse proxy is the gatekeeper that shapes and protects traffic before it reaches your code.
A reverse proxy may handle: TLS termination (completing the HTTPS encryption handshake, so your application servers don't each have to), request routing (sending /api one way and /images another), compression (shrinking responses to save bandwidth), caching (holding certain responses near the backend boundary), security filters (blocking malicious or malformed requests), rate limiting (stopping one client from overwhelming the system), and header normalization and request/response transformations.
Here's the practical distinction that matters, because these two get conflated constantly: a load balancer focuses on distributing traffic across healthy backends — its core question is "which server?" A reverse proxy focuses on controlling and shaping HTTP traffic before it reaches the application — its core question is "what should happen to this request on the way in?"
In real systems, one product often performs both roles — many popular tools are load balancer and reverse proxy at once. That's fine. The conceptual difference still matters, because when you're designing or debugging, you want to know which job you're reasoning about: distributing load, or shaping traffic.
Step 7: Application Server — Where Business Logic Runs
Finally, after all of that, the request reaches the application server — the place where your code actually runs. This is the part most developers think of as "the system," and it's where a request like this gets handled:
GET /products/123
Here, the application server may authenticate the user (who are you?), authorize the access (are you allowed to see product 123?), validate the request, run the business logic, fetch data it needs, call downstream services if required, and build the response.
This is genuinely important work — it's where your product's actual behavior lives. But step back and notice what just happened to get here: by the time your code runs, the request has already passed through DNS, possibly a CDN, a load balancer, and a reverse proxy. Multiple infrastructure layers did real work — translation, routing, distribution, security, TLS — before a single line of your application logic executed.
The application server is important. But it is not the whole system. It's one participant — the one where business logic happens to live — in a much larger journey. That realization changes how you design and how you debug, and we'll come back to it.
Step 8: Caching Is Not One Layer
This section corrects one of the most common and most limiting beginner mental models: the belief that "cache" means one thing — a Redis instance sitting between the application and the database.
That's a cache. It is very far from the cache. In reality, caching happens at many layers along the request journey, each solving a different problem:
Browser Cache
↓
CDN Cache
↓
Reverse Proxy Cache
↓
Application Cache / Redis
↓
Database / Internal Cache
Walk them:
- Browser cache — stores assets right on the user's device. The fastest possible cache, because the request never even leaves the machine.
- CDN cache — stores content at edge locations near users globally (exactly what we saw in Step 4). Serves the world without touching your origin.
- Reverse proxy cache — can hold selected server responses near the backend boundary, answering repeated requests before they reach the application.
- Application cache (e.g. Redis) — stores computed results and hot data close to the application, so it doesn't recompute or re-query constantly. This is the one beginners think of.
- Database / internal cache — the database engine itself caches pages and indexes in memory, answering many queries without hitting disk.
Here's the crucial point: these are not the same cache in different spots. They are different caches solving different problems — and they differ in who owns them, how they're invalidated, what freshness they guarantee, and how they fail. The browser cache going stale is a very different problem from the database's page cache being cold, which is different again from a stale CDN object.
Understanding that caching is layered — not a single box — is one of the mental upgrades that separates someone who's read about caching from someone who can actually design with it. The full treatment of how caching works, and how to reason about all these layers, is the subject of Blog 6. For now, the upgrade is simply this: cache is a strategy that appears all along the journey, not a single component in the middle.
Step 9: Database — The Source of Truth
Eventually, for anything that must be remembered, the request reaches the database. This is where data lives durably — the system's long-term memory: users, products, orders, payments, messages, metadata, configuration.
Here's the defining idea. Almost everything else in the request journey is, in a sense, disposable. Caches can be rebuilt from scratch. Application servers can be killed and replaced with identical copies. CDN edges can be repopulated. But the database usually holds the source of truth — the authoritative record that, if lost, cannot simply be regenerated. That's why we protect it so carefully.
And that importance is exactly why databases so often become bottlenecks: nearly every request eventually needs data from it, writes require durability (the data must be safely persisted, which takes real work), indexes speed up reads but cost storage and slow down writes, complex queries can become expensive as data grows, and fundamentally, scaling state is harder than scaling stateless servers — you can clone a stateless application server trivially, but you can't just clone a database and expect all the copies to stay consistent.
That last line is one of the deepest truths in system design, and it's why so much of the craft is about protecting the database — with caches, with replicas, with careful data modeling. We won't go deeper here, because data and databases are the entire subject of the next article. For now, just anchor the idea: the database is usually the source of truth, and precisely because of that, it's usually the hardest thing to scale.
The Return Journey
We followed the request all the way in. But the story isn't over — the response has to travel back out, and it retraces the path in reverse:
Database / Cache
↓
Application Server
↓
Reverse Proxy
↓
Load Balancer
↓
CDN
↓
Browser
And along the way, more work happens: the response may be compressed to save bandwidth, it may be cached if it's safe to reuse (so the next identical request is faster), it gets logged and observed so the system can be monitored, it returns over the existing connection that was already established, and finally the browser renders it into the page the user sees.
The user perceives all of this — the full round trip, out and back, across every layer — as a single instant. That's the whole illusion the request journey creates: enormous coordinated machinery, experienced as one seamless moment.
What Changes When Traffic Grows?
It's worth pausing on why all these layers exist, because at small scale, they don't need to. A hobby project or an internal tool can genuinely look like this and work perfectly well:
User
↓
Server
↓
Database
One server, one database. No CDN, no load balancer, no reverse proxy. And that's correct for its scale — adding all the layers would be over-engineering (Part 2's warning about imaginary scale).
But watch what happens as it grows into a real production system:
Users
↓
DNS / Global Routing
↓
CDN
↓
Load Balancer
↓
Reverse Proxy / API Gateway
↓
Application Layer
↓
Cache Layer
↓
Database Cluster / Storage Systems
The simple three-box system becomes the full journey — not because someone wanted complexity, but because growth forced each layer into existence: more users means you need load balancing across many servers; more regions means you need DNS routing and CDN edges for global speed; more data means you need caching layers and a scaled-out database; more traffic spikes means you need the capacity to absorb bursts; more failure modes means you need health checks, failover, redundancy; more observability needs means you need to actually see what's happening; more cost pressure means you need CDNs and caches to keep the bill sane.
This is the moment the building blocks from Part 1 stop being a vocabulary list and start being real — each one earning its place in the architecture because a specific pressure of scale demanded it. The full journey isn't complexity for its own sake. It's what a simple system becomes when it succeeds.
Failure Thinking Along the Request Path
Part 2 taught us to expect failure. Now that we can see the whole path, we can apply that mindset concretely — because every single layer is a place something can go wrong:
- what if DNS is misconfigured? Users can't even find you — every server could be healthy and the site is still unreachable.
- what if a CDN edge is unhealthy? Users routed to it get errors or slowness until they're steered elsewhere.
- what if CDN cache misses spike? Suddenly the origin gets flooded with traffic the CDN was supposed to absorb.
- what if the load balancer sends traffic to unhealthy instances? Requests fail even though healthy servers exist right next to the broken ones.
- what if app servers are saturated? Requests queue, slow down, and time out.
- what if the cache is down? Every request that the cache was absorbing now slams the database directly.
- what if the database is slow? The slowness cascades all the way back up the chain to the user.
- what if observability is missing? All of the above happens, and you're debugging blind.
Notice the pattern that emerges: every layer that improves performance or availability also introduces operational responsibility. The CDN makes you faster — and now you have edge health and cache invalidation to manage. The load balancer makes you resilient — and now health checks can misfire. The cache makes you fast — and now you have a cold-cache failure mode. There's no free layer; every one you add is a capability and a new thing that can break.
We're not going to design the defenses here — retries, circuit breakers, failover, graceful degradation are a whole discipline that gets a dedicated article later in the series. The point for now is the mindset: as you look at the request journey, don't just see how it works when everything's healthy. See the eight places it can break — because production eventually will.
From My Journey: An Architectural Lesson
Early in an engineering career, it's natural to believe the application server is the system. That's where your code lives, after all. A request hits the backend, your code runs, the database returns data, a response goes back. The whole world of a system, in that mental model, is the part you wrote.
Real projects erode that belief, usually through debugging.
Because eventually something breaks that has nothing to do with your code — and if the request path is a black box to you, you'll waste hours looking in the wrong place. The page is slow, and you comb through your application logic for the bottleneck… and it turns out a CDN edge was misbehaving, or a DNS change hadn't propagated, or the cache was cold and hammering the database, or the load balancer was routing to a sick instance. The code was fine the whole time. The system had a problem, one layer away from the code.
That's the shift. You start to see that before a request ever reaches your application, it may pass through DNS, a CDN, a load balancer, a reverse proxy, security filters, and cache layers — and after your code responds, the reply travels back through several of those same layers before the user sees anything. The application isn't the system. It's one participant in a much larger journey, and problems can live in any part of it.
Understanding the full path changed two things at once: how I debugged, and how I designed. Debugging, because I finally knew where to look — which layer to suspect for which symptom. And design, because I could reason about latency, failover, caching, and security as properties of the whole journey, not just my slice of it.
The application is where business logic runs. The system is everything that lets a user reach it safely, quickly, and reliably.
That's the lesson worth carrying out of this article. Your code matters enormously — but it's the destination of a long journey, not the journey itself. Learn the whole path, and you become the engineer who can actually reason about the whole system.
Why This Journey Matters for System Design
Let's make the payoff explicit, because understanding this path isn't trivia — it's a tool you'll use constantly. A clear mental model of the request journey directly helps you with: debugging latency (you know which layer to check for which kind of slowness), identifying bottlenecks (you can reason about where load actually concentrates), designing cache strategy (you understand the layers where caching can live), understanding global performance (DNS, CDN, and edge behavior stop being mysteries), planning failover (you can see every point that needs a backup plan), choosing where to enforce security (TLS, filtering, rate limiting each have a natural home), explaining architecture diagrams (the boxes finally mean something concrete), and preparing for future system designs (every system we design later sits on this foundation).
Here's the bottom line, and it's why this article comes before all the big system designs: before designing Netflix, Amazon, WhatsApp, or any large platform, you have to understand the journey of one request. Every one of those systems is, at its core, an elaborate arrangement of the exact layers we just walked — tuned, scaled, and specialized for their particular problem. Master the journey of a single request, and the grandest architectures become variations on a path you already know.
Key Takeaways
- A request passes through many systems — DNS, CDN, load balancer, reverse proxy — before your application code ever runs.
- DNS translates human-friendly names into the IP addresses the network actually routes on, through a cached, hierarchical lookup chain.
- DNS TTL is a trade-off: low TTL enables fast failover and steering but adds DNS load; high TTL is efficient but slows change.
- A CDN serves content from edges close to users — a cache hit is fast and skips the origin; a cache miss fetches from origin and stores a copy.
- Users reach CDN edges via DNS-based steering, Anycast routing, or a combination — and the chosen edge is the nearest healthy one, not always the geographically closest.
- Load balancers distribute traffic across healthy backends, enabling horizontal scaling and availability — the algorithms are a deep dive of their own.
- Reverse proxies shape and protect HTTP traffic (TLS, routing, compression, security, rate limits) — often the same component as the load balancer, but a distinct role.
- Caching exists at many layers — browser, CDN, reverse proxy, application, and database — each with different owners, invalidation rules, and failure modes.
- Databases are usually the source of truth, and precisely because state is hard to scale, they often become the bottleneck.
- Understanding the full request path improves your debugging and your architecture judgment — every large system is built on this one journey.
What's Next
You now understand how a request travels to your application and back — the physical path of the internet. But that raises a deeper question, one that sits underneath every system we'll design from here on: what is the system actually trying to protect and remember?
Everything in the journey we just walked exists, ultimately, to move and safeguard one thing: data. So before we design real systems, we need to understand data itself — why it's the most valuable, longest-lived asset in any system, and how architects choose between SQL and NoSQL once they understand what they're actually storing.
Blog 5 — Data and Databases: From Persistence to SQL, NoSQL, and Source of Truth. The next article covers data as the long-lived asset that outlasts every server and cache, why persistence matters and memory alone isn't enough, stateful vs. stateless systems, structured vs. semi-structured vs. unstructured data, the concept of a source of truth, and how all of that leads directly into choosing correctly between SQL and NoSQL.
Question for Readers
Now that you've seen the whole path, think back on your own debugging war stories:
When you debug a slow request, which layer do you check first? And have you ever confidently blamed your application code — only to discover the real problem was DNS, the CDN, a cache, the load balancer, or the database?
Almost every engineer has one of those stories. Which part of the request journey was most invisible to you before you started thinking in systems?