WebArchitecture • Backend • Frontend • DNS • CDN • API • SystemDesign

How the Web Actually Works: A Developer's Mental Model of Modern Architecture

Aug 8, 202611 min read


I spent a long time writing code before I genuinely understood what happened after I hit Enter on a URL. I knew about frontend and backend. I had a vague sense that CDNs existed. But if someone asked me to draw the path a request takes from a browser to a database and back — I would have drawn something embarrassingly incomplete.

This post is the explanation I wish I had early on. No Docker. No Kubernetes deep-dives. Just the foundational mental model of how modern web architecture actually fits together — the pieces every developer should be able to reason about clearly.


What Actually Happens When You Type a URL

Let us start with the most fundamental question: you open a browser, type https://example.com, and press Enter. What happens?

Most people say "the browser loads the page." That is true in the same way that "a car moves" describes a Formula 1 race. The real answer involves at least six different systems activating in under a second.

Step 1 — DNS Resolution

Your browser does not understand names like example.com. It only speaks in IP addresses — things like 104.21.5.8. So the first thing it does is ask a system called DNS (Domain Name System) to translate:

Browser:  "Where is example.com?"
DNS:      "It is at 104.21.5.8"

DNS is essentially a phone book for the internet. The key thing to understand is that the IP address DNS returns is usually not your actual application server. It almost always belongs to a CDN, a load balancer, or a reverse proxy sitting in front of your real infrastructure. More on those shortly.

Step 2 — HTTPS and Encryption

Once the browser has an IP, it does not just start firing data at it. It first sets up an encrypted channel using TLS (the protocol behind HTTPS). Think of this as sealing your request in an envelope that only the destination server can open.

Without this encryption, anyone on the same network — say, someone on the same coffee shop WiFi — could read everything you send: passwords, session cookies, tokens, form data. HTTPS makes the entire conversation private.

Step 3 — The Edge Layer

Here is something most tutorials skip over. Your request does not go straight to your app. It hits what is called the edge layer first — a set of systems sitting between the public internet and your actual servers.

SystemWhat it does
CDN (Cloudflare, CloudFront)Caches files near the user, absorbs load
Load BalancerSpreads traffic across multiple servers
Reverse ProxyRoutes requests, hides backend infrastructure
WAFBlocks malicious traffic before it reaches your app

This layer is doing real work: blocking attack traffic, serving cached files without ever touching your application, routing requests to the right place, and encrypting/decrypting connections. By the time a request actually reaches your application, it has already been filtered and directed.

Steps 4–6 — Application, Database, Response

The request finally reaches your application server, which processes it: checks if you are logged in, queries a database, applies business logic, and assembles a response. That response — HTML, CSS, JavaScript, or JSON — travels back the same chain to your browser, which renders what you see.

The full journey:

You type example.com
       │
       ▼
DNS  →  IP address returned
       │
       ▼
HTTPS connection opened (encrypted)
       │
       ▼
Edge layer (CDN / Load Balancer / Reverse Proxy)
       │
       ▼
Application server
       │
       ▼
Database (if needed)
       │
       ▼
Response travels back
       │
       ▼
Browser renders the page

All of that happens in roughly 50–200 milliseconds. Every step matters.


The Most Important Split: Frontend vs Backend

Every web system is divided into two distinct worlds, and confusing them causes real architectural mistakes.

Frontend is everything the user sees and interacts with — buttons, layouts, forms, animations. In modern development, frontend code (React, Vue, Angular) runs inside the user's browser, not on your server. Once the browser downloads your JavaScript bundle, it executes locally on the user's device. Your server is no longer involved in rendering the UI.

Backend is everything that runs on your server, hidden from users. It handles things that require authority and trust:

  • Authenticating who you are
  • Checking what you are allowed to do
  • Reading and writing to databases
  • Processing payments
  • Sending emails
  • Enforcing business rules

The single most important distinction between these two:

Frontend:  Runs in the USER's browser — never trust it with secrets
Backend:   Runs on YOUR server — this is where authority lives

This is not just a conceptual distinction. It has real security consequences. Frontend code can be read by anyone — open DevTools, open the source, everything is visible. Sensitive logic, secret keys, and authorization decisions must always live on the backend. The frontend presents information; the backend enforces rules.


Static vs Dynamic Content

Not all content on the web is the same kind of thing, and understanding the difference shapes every hosting decision you make.

Static content is identical for every single user who requests it. Your JavaScript bundle, CSS files, images, fonts — these files do not change based on who is asking. They are the same for a first-time visitor in Delhi and a returning user in Berlin.

Dynamic content is generated fresh for each request, personalised per user. Your dashboard data, account balance, notification count, order history — this content is unique and cannot be pre-baked.

Why does this matter? Because the strategies for serving them are completely different:

Static content  →  Serve from CDN (fast, cheap, globally distributed)
Dynamic content →  Must go through your backend server (personalised, cannot be cached naively)

Smart architecture offloads as much as possible to static delivery and keeps the backend focused only on work that genuinely requires computation or personalisation.


SPA vs SSR: Two Different Ways to Build a Frontend

There are two dominant approaches to building web frontends, and they have meaningfully different architectures.

SPA — Single Page Application

A SPA (React, Vue in standard mode) works like this: the browser downloads the entire frontend application once — HTML, CSS, a JavaScript bundle — and React boots up inside the browser. From that point, all navigation happens inside the browser. When data is needed, React calls your backend API, gets JSON back, and updates the page without a full reload.

First visit:
  Browser → Downloads React app (index.html + main.js)

All subsequent interactions:
  React (in browser) → calls API → backend returns JSON → React updates UI
  No full page reloads. The server's job is essentially done after the initial download.

The big upside: smooth, app-like experience. The static files sit perfectly on a CDN. Frontend and backend are completely independent.

The known tradeoffs: the first load requires the browser to download and execute JavaScript before anything renders, which can feel slow. Search engines historically struggled with pages that were empty HTML shells populated only after JavaScript ran — though this has improved significantly.

SSR — Server-Side Rendering

SSR (Next.js, Nuxt) addresses those tradeoffs by doing the rendering work on the server before sending content to the browser:

Browser requests /product/123
Server runs React, fetches product data, generates complete HTML
Browser receives ready-to-display HTML immediately
JavaScript loads in background and makes page interactive

The user sees real content almost immediately. Search engine crawlers see actual content, not an empty <div id="root">. The tradeoff is that SSR requires a running server — you cannot just put it on a CDN and walk away.

Most modern frameworks like Next.js support both modes at once: some pages pre-rendered at build time, others server-rendered on each request, others rendered purely in the browser. You pick per page.


API-Driven Architecture: Why Frontend and Backend Are Separate

Older web applications were tightly coupled — the server generated complete HTML pages and sent them to the browser. Modern systems separate frontend and backend into independent services that communicate through APIs.

An API (Application Programming Interface) is simply a set of URLs your backend exposes that return structured data, almost always JSON:

Frontend calls:  GET /api/user/42
Backend returns: { "id": 42, "name": "Priya", "email": "[email protected]" }
Frontend renders: a profile card with that data

This separation pays dividends in multiple ways. The same backend API serves your web frontend, your iOS app, your Android app, and any third-party integrations — all consuming the same endpoints. Frontend teams deploy independently of backend teams. You can rewrite the frontend in a completely different framework without touching the backend.

The principle here is loose coupling — components that communicate through clean interfaces rather than being tangled together. It is one of the most important architectural patterns in modern software.


CDN: Why the Web Can Be Fast for Everyone

Without a CDN, every user in the world downloads files from your one server. A user in Frankfurt downloading your JavaScript from a server in Mumbai is adding 150–200ms of network round-trip latency to every asset, before their browser even starts executing anything.

A CDN (Content Delivery Network) solves this by maintaining hundreds of servers — called edge nodes — distributed globally. When you upload your static files to a CDN, it replicates them across these locations:

Without CDN:
  User in Frankfurt → Server in Mumbai → 180ms just in network travel

With CDN:
  User in Frankfurt → CDN edge in Frankfurt → 5ms
  User in Delhi     → CDN edge in Delhi     → 3ms
  User in New York  → CDN edge in New York  → 4ms

The first time a user in Frankfurt requests your main.js, the CDN fetches it from your origin server and caches it locally. Every user after that — from Frankfurt or anywhere nearby — gets it directly from that edge node. Your origin server is not involved at all.

This is what "cache hit" means: the file was already at the nearest edge. At scale, a well-configured CDN will serve 95%+ of static file requests from cache, leaving your origin server mostly untouched.

One clarification worth making explicit: CDNs are excellent at serving static content. They are not designed for your dynamic API responses that contain personal user data. Those must still reach your backend.


Reverse Proxies: The Traffic Directors

A reverse proxy is a server that sits in front of your application and mediates all incoming traffic. Users talk to the proxy; the proxy talks to your app. Users never contact your application directly.

Internet → Reverse Proxy → Your Application

Nginx is the most common reverse proxy, and it earns its place by handling several things your application should not have to deal with:

TLS termination — the reverse proxy handles HTTPS decryption so your application code receives plain HTTP. You manage one certificate, at the proxy, not inside every service.

Routing — requests to /api/* go to the backend server, requests to / go to the frontend server. One proxy, multiple destinations.

Compression — the proxy compresses responses automatically. A 500KB JSON payload can compress to 80KB, reducing transfer time by 84%.

Rate limiting — block clients making too many requests before they ever touch your application code.

Buffering — this one is subtle but important. Without buffering, slow mobile clients keep Gunicorn workers busy for the entire duration of the download, even though Django finished generating the response in 10ms. With buffering, the proxy receives the full response instantly, frees the Gunicorn worker, and then slowly drips data to the slow client. Your application becomes far more concurrent.


Load Balancers: Distributing the Work

When traffic grows beyond what one server can handle, you run multiple copies of your application and use a load balancer to distribute requests across them:

Users
  │
  ▼
Load Balancer
  ├── Server 1
  ├── Server 2
  └── Server 3

The load balancer tracks which servers are healthy and routes new requests to available ones. If Server 2 goes down, the load balancer stops sending it traffic automatically. When it recovers, it is added back.

This setup also enables something powerful: you can take servers offline for updates without any downtime. Take Server 1 offline, update it, bring it back, then do the same for Server 2. Traffic continues flowing to the remaining healthy servers throughout.

Load balancers are also the entry point for autoscaling. At low traffic: two servers. Traffic spikes: add four more. Load balancer starts including them immediately.


Stateless vs Stateful: The Architectural Divide That Shapes Everything

This concept shows up in every conversation about scaling, and it is worth understanding precisely.

A stateless system stores no important data locally. Every request it receives is self-contained — the server does not need to remember anything about previous requests to handle the next one. If you have three copies of a stateless backend running, any of them can handle any request. Kill one, start another, it picks up immediately.

A stateful system holds critical persistent data. A database is stateful. If your PostgreSQL instance disappears, so does every user record, every order, every piece of data your product is built on. You cannot simply start a fresh copy and continue — the data is gone.

Stateless → Can restart anytime, scale freely, failures are trivial to recover
Stateful  → Requires persistent storage, backups, replication, failover plans

The golden rule of modern web architecture follows from this: make your application logic stateless. Store all persistent data in dedicated external systems — databases, cache stores, object storage. Your application servers then become disposable: scale them up, scale them down, replace crashed ones, deploy new versions — none of it risks data loss because there is no important data inside them.

A concrete example: if a user uploads a profile photo and your backend stores it on the server's local filesystem, you have a stateful application. When that server restarts or gets replaced, the photo is gone. If instead the backend uploads the photo to object storage (S3, R2, or similar) and stores only the URL in the database, the server becomes stateless again — it holds nothing of value that cannot be recovered.


The Three-Layer Production Architecture

Modern production systems are almost always organised into three distinct layers. Understanding this structure helps you reason about where things live and why.

DELIVERY LAYER
  CDN (Cloudflare, CloudFront)
  Serves static files from hundreds of global locations
  React app, CSS, images, fonts

         │
         ▼

COMPUTE LAYER
  Application servers
  Backend APIs, business logic, authentication
  WebSocket services for realtime features
  Background workers for async jobs

         │
         ▼

DATA LAYER
  PostgreSQL (relational data — users, orders, content)
  Redis (fast cache, sessions, realtime pub/sub)
  Object Storage (uploaded files, images, documents)

The delivery layer is cheap and globally fast — CDN infrastructure is designed to handle enormous traffic with minimal cost. The compute layer is stateless and scales horizontally — add more servers during peaks, remove them during quiet periods. The data layer is stateful and requires the most careful management — this is where persistence, replication, and backups live.

Here is what a real request looks like flowing through this:

A user loads your React app:

Browser → CDN edge (nearest city) → serves main.js from cache → React boots up

That user logs in:

Browser → Cloudflare → Load Balancer → Backend API
        → database query (verify credentials)
        → returns auth token to browser

React fetches dashboard data:

Browser → Load Balancer → Backend API
        → Redis (check cache — is this data already stored?)
        → Cache miss → PostgreSQL query
        → Store result in Redis for next request
        → Return JSON → React renders the page

Each layer does what it is best at. Static files never touch your backend. Dynamic data never hits the CDN. Frequently-accessed data gets cached in Redis so the database is not queried repeatedly for the same thing.


The Mental Models That Actually Matter

After understanding all of the above, a few core ideas stand out as the ones worth internalising:

The web is many systems, not one. A single URL request involves DNS, TLS negotiation, CDN caching, load balancing, application processing, and database queries. Understanding which layer is responsible for what makes debugging and architecture decisions dramatically clearer.

Frontend and backend are separated by design. The browser runs your UI. Your server runs your logic. Never put secrets or authorization decisions in the frontend — it is visible to everyone.

Static content and dynamic content need different strategies. Files that are the same for everyone belong on a CDN. Data that is personalised or changes frequently must be generated by the backend.

Stateless scales, stateful requires care. Design your application servers to hold no important data. Let databases and dedicated storage systems handle persistence. This is not just good practice — it is what makes modern scaling possible.

The CDN is not optional at scale. Serving files from one server to users on every continent is slow and expensive. CDNs exist to solve this, and they solve it well.


What Comes Next

This is the foundation. The natural next questions — how are applications packaged and deployed, how do they handle thousands of simultaneous users, how does authentication work in distributed systems, how do you build real-time features — all of them build directly on the concepts here.

Once you can picture the full request journey, the layers of a production system, and why stateless design matters, the rest of modern web infrastructure becomes considerably less mysterious.

The concepts have names: containers, orchestration, JWT, WebSockets, message queues. But underneath the vocabulary, they are all answers to the same fundamental questions: how do you serve the right content to the right user, reliably, at scale, without losing data? The architecture described here is how the industry answered those questions.

← Back to Blog