HTTP Overview
Web Architecture
Read aloud is unavailable in this browser.
The Invisible Machine Behind Every Click
When you type a URL and press Enter, an extraordinary orchestration begins. Within milliseconds, dozens of systems across the globe coordinate to deliver content to your screen. DNS servers translate names to addresses. CDN edge nodes check caches. Load balancers distribute requests. Application servers process logic. Databases retrieve data. Reverse proxies compress and encrypt. Browsers parse, render, and execute.
This is web architecture—the intricate system of components that transforms a simple URL into a fully rendered, interactive web page.
Understanding web architecture is essential for building systems that scale, debugging production issues, and making informed infrastructure decisions. A URL might seem like a direct line to a server, but in reality, it traverses a complex ecosystem carefully designed for performance, reliability, and security.
This page maps the complete web architecture: every major component, how they interact, and why each exists. You'll finish with a comprehensive mental model of web infrastructure—from browser to server and back.
By the end of this page, you will understand the complete request journey from browser to server and back, the role of each infrastructure component (CDNs, load balancers, proxies, caches), how modern web architectures achieve global scale and high availability, browser architecture and rendering pipeline, and practical patterns for designing robust web systems.
The Complete Request Journey
Let's trace a complete request journey, from URL entry to rendered page. This reveals the full architecture in context.
Step 1: URL Parsing
You enter https://shop.example.com/products/shoes
The browser parses this into components:
- Scheme:
https(protocol) - Host:
shop.example.com - Path:
/products/shoes
Step 2: DNS Resolution
The browser needs an IP address for shop.example.com:
- Check browser DNS cache → Cache miss
- Check OS DNS cache → Cache miss
- Query local DNS resolver (ISP or configured) → Cache miss
- Resolver queries root DNS servers → Referral to
.comTLD - Resolver queries
.comTLD servers → Referral toexample.comauthoritative - Resolver queries
example.comauthoritative → Returns IP:93.184.216.34 - Response cached at each level for TTL duration
Actual resolution: typically 20-100ms if uncached, <1ms if cached.
Step 3: Connection Establishment
Browser initiates connection to 93.184.216.34:443:
- TCP 3-way handshake (SYN, SYN-ACK, ACK)
- TLS handshake (ClientHello, ServerHello, certificates, key exchange)
- HTTP/2 negotiated via ALPN
Connection to CDN edge node in nearby city, not necessarily origin server.
Step 4: HTTP Request
GET /products/shoes HTTP/2
Host: shop.example.com
Accept: text/html,application/xhtml+xml
Accept-Encoding: gzip, br
Cookie: session=abc123
User-Agent: Mozilla/5.0...
Step 5: CDN Processing
Request arrives at CDN edge node:
- Check edge cache for
/products/shoes→ Cache miss (dynamic content) - Forward to origin (via CDN backbone, optimized routes)
Step 6: Load Balancer Distribution
Request reaches load balancer:
- Health check: which app servers are available?
- Algorithm selects target (round-robin, least-connections, etc.)
- Request forwarded to App Server 3
Step 7: Application Processing
App Server 3 processes request:
- Parse request, extract path parameters
- Authenticate user from session cookie
- Query database for product details
- Render HTML template with data
- Return response
Step 8: Response Journey
Response travels back:
- App Server → Load Balancer → CDN Edge → Browser
- CDN may cache static portions
- Compression (gzip/brotli) applied
- TLS encryption for transit
Step 9: Browser Processing
Browser receives HTML:
- Parse HTML, construct DOM
- Discover additional resources (CSS, JS, images)
- Fetch resources in parallel (many are CDN-cached)
- Parse CSS, construct CSSOM
- Execute JavaScript
- Render page
Total Time Budget:
| Phase | Typical Time |
|---|---|
| DNS Resolution | 0-100ms (cached: <1ms) |
| TCP/TLS Handshake | 50-200ms |
| Request to edge | <10ms |
| Edge to origin | 50-200ms |
| Server processing | 20-500ms |
| Response transit | 20-100ms |
| Browser parsing/rendering | 100-500ms |
| Total | 200ms-1.5s typical |
CDN caching eliminates origin roundtrips for cacheable content (static assets, some HTML). Server processing is often the largest variable—a slow database query dominates the time budget. Browser parallelism (HTTP/2) minimizes resource fetch delays.
Browser Architecture
Browsers are complex applications with multiple specialized components. Understanding browser architecture explains performance behaviors and debugging strategies.
Major Browser Components:
1. User Interface The browser chrome: address bar, bookmarks, navigation buttons. Not the web content area—that's rendered by the rendering engine.
2. Browser Engine Orchestrates between UI and rendering engine. Manages navigation, history, and coordinates components.
3. Rendering Engine
- Blink (Chrome, Edge, Opera)
- WebKit (Safari)
- Gecko (Firefox)
Parses HTML and CSS, constructs the DOM and CSSOM, calculates layout, and paints pixels.
4. JavaScript Engine
- V8 (Chrome, Edge, Node.js)
- SpiderMonkey (Firefox)
- JavaScriptCore (Safari)
Parses, compiles, and executes JavaScript. Modern engines use JIT (Just-In-Time) compilation for performance.
5. Networking Handles HTTP/HTTPS, WebSocket, and other network protocols. Implements connection pooling, caching, cookie management, and security.
6. UI Backend Draws basic widgets (input boxes, buttons) using native OS capabilities.
7. Data Storage Manages cookies, localStorage, sessionStorage, IndexedDB, and cache storage.
| Stage | Input | Output | Blocking? |
|---|---|---|---|
| HTML Parsing | HTML bytes | DOM tree | Yes - must complete for rendering |
| CSS Parsing | CSS bytes | CSSOM tree | Yes - blocks rendering |
| Style Calculation | DOM + CSSOM | Styled elements | Required for layout |
| Layout | Styled elements | Element positions/sizes | Required for paint |
| Paint | Layout tree | Paint records (draw commands) | Creates visual representation |
| Composite | Paint records + layers | Final pixels | GPU-accelerated |
The Critical Rendering Path:
Browsers render pages through a defined pipeline:
- Parse HTML → DOM: Build the Document Object Model representing page structure
- Parse CSS → CSSOM: Build the CSS Object Model representing styles
- Combine → Render Tree: Match styles to DOM elements
- Layout: Calculate element positions and dimensions
- Paint: Generate pixel instructions for each element
- Composite: Combine layers and display to screen
Render-Blocking Resources:
-
CSS is render-blocking: Browser won't render until CSSOM is complete. Put CSS in
<head>, load critical CSS first. -
JavaScript can block parsing: Scripts in
<head>block HTML parsing unlessasyncordefer. Put scripts at end of<body>or use async/defer.
<script src="app.js"></script> <!-- Blocks parsing -->
<script src="app.js" async></script> <!-- Doesn't block, runs when ready -->
<script src="app.js" defer></script> <!-- Doesn't block, runs after parsing -->
Resource Priorities:
Browsers prioritize resource loading:
- Highest: Main HTML document, critical CSS
- High: Visible images, synchronous scripts
- Medium: Fonts, preloaded resources
- Low: Prefetched resources, below-fold images
HTTP/2 priorities let servers optimize delivery order.
Browser DevTools reveal architecture in action. The Network tab shows request timing (queued, DNS, connection, TLS, TTFB, download). The Performance tab shows the rendering pipeline. The Application tab shows storage. Use these to diagnose bottlenecks.
Content Delivery Networks (CDNs)
CDNs are geographically distributed networks of servers that cache and serve content close to users. They're fundamental to modern web performance.
The Latency Problem:
Speed of light imposes physical limits. Data center in Virginia, user in Tokyo:
- Distance: ~11,000 km
- Speed of light (fiber): ~200,000 km/s
- Minimum one-way delay: ~55ms
- Round-trip: ~110ms
For each request-response, 110ms is unavoidable physics. Multiple round-trips compound this.
CDN Solution:
CDNs place edge servers globally. User in Tokyo connects to Tokyo edge node:
- Distance: ~50 km
- Latency: ~1ms
Edge serves cached content immediately—no origin roundtrip. Uncached requests still go to origin, but even then, CDN backbone optimization helps.
CDN Functionality:
- Caching: Store copies of static content (images, CSS, JS, videos)
- Edge Computing: Run code at edge (Cloudflare Workers, Lambda@Edge)
- Load Balancing: Distribute origin load across servers
- DDoS Protection: Absorb attack traffic before reaching origin
- SSL Termination: Handle TLS handshakes at edge
- Compression: gzip/Brotli compression at edge
- Image Optimization: Resize, format-convert images on-the-fly
CDN Caching Strategy:
CDN caching is header-driven:
Cache-Control: public, max-age=31536000, immutable
public: CDN can cachemax-age=31536000: Cache for 1 yearimmutable: Don't revalidate even on refresh
Best practices:
- Static assets (CSS, JS, images): Long max-age with asset versioning (
app.v2.js) - HTML: Short max-age or
no-cache(always revalidate) - API responses: Vary by authorization, short TTL or no cache
Cache Invalidation:
The hardest problem in CDN:
- Purge: Remove specific URL from all edges
- Versioning: Change filename (
app.v2.css) → old cached, new fetched - Soft Purge: Mark stale, serve while revalidating
- TTL Expiration: Wait for max-age to expire
Popular CDNs:
| CDN | Strengths |
|---|---|
| Cloudflare | Free tier, DDoS protection, Workers edge compute |
| AWS CloudFront | AWS integration, Lambda@Edge |
| Akamai | Enterprise, largest network, security |
| Fastly | Instant purge, edge compute, configuration flexibility |
| Google Cloud CDN | GCP integration, global anycast |
For most applications, CDN should handle all static assets and, where possible, cache HTML. Configure long cache TTLs with asset versioning. Edge compute (Cloudflare Workers, Lambda@Edge) can handle personalization, A/B testing, and authentication at the edge—reducing origin load dramatically.
Load Balancers
Load balancers distribute incoming requests across multiple backend servers, enabling horizontal scaling and high availability.
Why Load Balancing?
-
Scalability: Single server capacity is limited. Load balancing distributes work across many servers.
-
Availability: If one server fails, load balancer routes to healthy servers. No single point of failure.
-
Maintenance: Update servers one at a time without downtime. Load balancer drains and excludes updating servers.
-
Performance: Route requests to least-loaded or geographically closest servers.
Load Balancing Algorithms:
| Algorithm | How It Works | Best For |
|---|---|---|
| Round Robin | Cycle through servers sequentially | Uniform servers, stateless workloads |
| Weighted Round Robin | Proportional to configured weights | Heterogeneous server capacity |
| Least Connections | Route to server with fewest active connections | Long-lived connections, varying request duration |
| Weighted Least Connections | Combines weights with connection count | Heterogeneous capacity + varying duration |
| IP Hash | Hash client IP to consistent server | Session affinity without cookies |
| Least Response Time | Route to fastest responding server | Latency-sensitive applications |
| Random | Random server selection | Simple, surprisingly effective |
Layer 4 vs Layer 7 Load Balancing:
Layer 4 (Transport Layer):
- Operates on TCP/UDP connections
- Sees: source IP, destination IP, ports
- Faster—minimal processing
- Can't inspect HTTP content
- Examples: AWS NLB, HAProxy (L4 mode)
Layer 7 (Application Layer):
- Operates on HTTP requests
- Sees: URL, headers, cookies, content
- More features—content-based routing, SSL termination
- Higher overhead—full HTTP parsing
- Examples: AWS ALB, Nginx, HAProxy (L7 mode), Envoy
Layer 7 Capabilities:
# Route by path
/api/* → API servers
/static/* → Static servers
/admin/* → Admin servers
# Route by header
Host: api.example.com → API cluster
Host: www.example.com → Web cluster
# Route by cookie
session_id present → Sticky to specific server
no session_id → Round-robin
Health Checks:
Load balancers continuously verify server health:
- Active checks: Periodic requests to health endpoint (
/health) - Passive checks: Monitor response codes from real traffic
- Unhealthy threshold: N consecutive failures → remove from pool
- Healthy threshold: M consecutive successes → add back to pool
# Healthy server
/health → 200 OK
# Unhealthy server (database connection lost)
/health → 503 Service Unavailable
Session Persistence (Sticky Sessions):
Some applications require requests from the same user to reach the same server (server-side sessions). Load balancers support:
- Cookie-based: Load balancer sets cookie identifying server
- IP-based: Hash client IP to consistent server
- Application affinity: Route by application-provided session ID
Warning: Sticky sessions reduce scaling flexibility and complicate failover. Prefer stateless designs.
Load balancers themselves must be highly available—they're on the critical path. Cloud load balancers (ALB, NLB) are inherently redundant. Self-hosted load balancers (HAProxy, Nginx) need failover pairs with virtual IPs or DNS failover.
Reverse Proxies and API Gateways
Reverse proxies sit between clients and servers, providing a unified interface while handling cross-cutting concerns. They're distinct from (though often combined with) load balancers.
Forward Proxy vs Reverse Proxy:
- Forward Proxy: Acts on behalf of clients (corporate proxy filtering outbound traffic)
- Reverse Proxy: Acts on behalf of servers (Nginx fronting application servers)
Reverse Proxy Functions:
1. SSL Termination Handle TLS encryption/decryption at the proxy. Backend servers receive unencrypted traffic, simplifying their configuration and reducing CPU load.
[Client] --HTTPS--> [Reverse Proxy] --HTTP--> [App Server]
2. Compression Compress responses (gzip, Brotli) before sending to clients. App servers send uncompressed, proxy compresses.
3. Static File Serving Serve static files directly without app server involvement. Nginx is extremely efficient at serving static content.
4. Request Routing Route requests to different backends based on path, headers, or other criteria.
5. Caching Cache responses to reduce backend load. Shared cache across multiple clients.
6. Rate Limiting Limit requests per client to protect backends from abuse.
7. Request/Response Modification Add headers, rewrite URLs, modify responses.
123456789101112131415161718192021222324252627282930313233343536373839404142434445
http { # Caching configuration proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g; # Compression gzip on; gzip_types text/html text/css application/javascript application/json; upstream app_servers { server app1:8080; server app2:8080; server app3:8080; } server { listen 443 ssl http2; server_name example.com; # SSL termination ssl_certificate /etc/ssl/certs/example.com.crt; ssl_certificate_key /etc/ssl/private/example.com.key; # Static files - served directly by Nginx location /static/ { root /var/www; expires 1y; add_header Cache-Control "public, immutable"; } # API routes - proxy to app servers location /api/ { proxy_pass http://app_servers; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Rate limiting for login endpoint location /api/login { limit_req zone=login_limit burst=5; proxy_pass http://app_servers; } }}API Gateways:
API gateways are specialized reverse proxies for API management:
| Function | Description |
|---|---|
| Authentication | Verify API keys, JWTs, OAuth tokens |
| Rate Limiting | Per-client request limits |
| Request Validation | Validate request format, schema |
| Request Transformation | Modify requests before backend |
| Response Transformation | Modify responses before client |
| Service Discovery | Find healthy backend instances |
| Circuit Breaker | Stop requests to failing services |
| Analytics | Track API usage, latency, errors |
Popular API Gateways:
- Kong: Open-source, plugin ecosystem
- AWS API Gateway: Managed, Lambda integration
- Nginx Plus: Commercial Nginx with API features
- Envoy: Cloud-native, service mesh capable
- Traefik: Cloud-native, Kubernetes-native
Service Mesh:
In microservices architectures, sidecar proxies (Envoy, Linkerd) handle service-to-service communication:
[Service A] ←→ [Sidecar Proxy] ←→ [Sidecar Proxy] ←→ [Service B]
Sidecars provide load balancing, retries, timeouts, observability, and mTLS between services—without application code changes.
A typical production stack: [CDN Edge] → [Load Balancer] → [Reverse Proxy/Gateway] → [App Server]. Each layer handles specific concerns. CDN handles caching and geography. Load balancer handles distribution. Reverse proxy handles SSL, compression, and routing. App server handles business logic.
Web Servers and Application Servers
The terms 'web server' and 'application server' are often conflated but represent distinct roles in web architecture.
Web Server:
A web server handles HTTP protocol basics:
- Accept connections
- Parse HTTP requests
- Serve static files
- Return HTTP responses
Examples: Nginx, Apache HTTP Server, Caddy, IIS
Pure web servers are efficient at static content but don't execute application logic.
Application Server:
An application server runs application code:
- Execute server-side logic
- Process database queries
- Generate dynamic responses
- Manage sessions and state
Examples depend on language:
- Node.js: Express, Fastify, Koa
- Python: Gunicorn + Django/Flask, Uvicorn + FastAPI
- Java: Tomcat, Jetty, Spring Boot embedded
- Ruby: Puma, Unicorn + Rails
- Go: Built-in
net/http - PHP: PHP-FPM (with Nginx/Apache)
Common Patterns:
- •Nginx handles HTTP, SSL, static files
- •Requests for dynamic content proxied to app server
- •App server focuses on application logic
- •Common with Python (Gunicorn), Ruby (Puma), PHP (PHP-FPM)
- •Benefits: Nginx efficiency for static; app server for dynamic
- •App server handles HTTP directly
- •CDN/Load Balancer in front for production
- •Simpler architecture for capable servers
- •Common with Node.js, Go, Java Spring Boot
- •Benefits: Fewer moving parts; sufficient for many apps
Concurrency Models:
How servers handle multiple simultaneous requests:
| Model | Description | Examples |
|---|---|---|
| Process per request | Fork new process for each request | Apache prefork, CGI |
| Thread per request | Pool of threads, one handles each request | Java servlets, Apache worker |
| Event loop | Single thread, non-blocking I/O | Node.js, Nginx, Envoy |
| Async/Await | Single or few threads, coroutines | Python asyncio, Go goroutines |
| Hybrid | Event loop with thread pool for blocking ops | Gunicorn workers, Uvicorn |
Event loop vs thread-per-request:
- Thread-per-request: Simple programming model; threads block waiting for I/O; many threads = memory overhead
- Event loop: Non-blocking I/O; single thread handles many connections; requires async programming model
For I/O-bound web applications, event loop or async models scale better. For CPU-bound work, thread/process pools parallelize computation.
Worker Processes:
Python and Ruby commonly use worker processes:
Gunicorn master process
├── Worker 1 (handles requests)
├── Worker 2 (handles requests)
├── Worker 3 (handles requests)
└── Worker 4 (handles requests)
Each worker handles requests independently. Master manages worker lifecycle. Number of workers typically equals CPU cores.
Modern deployments often abstract servers entirely. Container orchestration (Kubernetes) manages server instances. Serverless platforms (Lambda, Cloud Functions) handle scaling automatically. The underlying web/app server concepts remain, but operational complexity shifts to platforms.
Caching Architecture
Caching is the most impactful performance optimization in web architecture. Avoiding work is faster than doing work efficiently.
Cache Layers:
Web requests traverse multiple cache layers:
- Browser Cache: Resources cached locally in the browser
- CDN Edge Cache: Cached at geographically distributed edge nodes
- Application Cache: In-memory cache (Redis, Memcached) for computed data
- Database Cache: Query results cached by database engine
- OS Cache: File system and network caches
Each layer has different scope, size, and invalidation characteristics.
HTTP Caching Headers:
| Header | Purpose | Example |
|---|---|---|
Cache-Control | Primary caching directive | public, max-age=3600 |
ETag | Resource version identifier | "abc123" |
Last-Modified | Last modification timestamp | Wed, 15 Jan 2025 08:00:00 GMT |
Vary | Cache varies by these request headers | Vary: Accept-Encoding, Accept-Language |
Expires | Absolute expiration (legacy) | Thu, 16 Jan 2025 08:00:00 GMT |
Cache-Control Directives:
Cache-Control: public, max-age=31536000, immutable
public: CDNs and browsers can cacheprivate: Only browser can cache (user-specific content)no-cache: Cache but revalidate before useno-store: Don't cache at allmax-age=N: Fresh for N secondss-maxage=N: Max-age for shared caches (CDN)immutable: Never revalidate (for versioned assets)stale-while-revalidate=N: Serve stale while fetching fresh
Conditional Requests (ETag/Last-Modified):
# Initial request
GET /product/123 HTTP/1.1
# Response with ETag
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: no-cache
...
# Later request - validate cache
GET /product/123 HTTP/1.1
If-None-Match: "abc123"
# If unchanged:
HTTP/1.1 304 Not Modified
# If changed:
HTTP/1.1 200 OK
ETag: "xyz789"
...new content...
Application Caching (Redis/Memcached):
Cache computed results to avoid repeated database queries:
def get_product(product_id):
# Try cache first
cached = redis.get(f"product:{product_id}")
if cached:
return json.loads(cached)
# Cache miss - query database
product = db.query("SELECT * FROM products WHERE id = ?", product_id)
# Populate cache for next request
redis.setex(f"product:{product_id}", 3600, json.dumps(product))
return product
Cache Invalidation Strategies:
- TTL Expiration: Set expiration; accept staleness
- Write-Through: Update cache on every database write
- Cache-Aside: Application manages cache; invalidate on write
- Event-Driven: Database changes trigger cache invalidation messages
'There are only two hard things in Computer Science: cache invalidation and naming things.' Stale caches cause subtle bugs. Use short TTLs for mutable data. Version immutable assets. Design for eventual consistency. Test cache invalidation explicitly.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.