Trace the evolution of HTTP from 1.1 to HTTP/3. Master request/response cycles, headers, caching, HTTP/2 multiplexing, HTTP/3's QUIC integration, WebSockets for real-time communication, and modern web performance optimization.
HTTP (Hypertext Transfer Protocol) is the foundation of the World Wide Web. From simple document retrieval in 1991 to powering complex web applications today, HTTP has evolved dramatically to meet modern demands for speed, security, and interactivity.
The evolution of HTTP: from simple document retrieval in 1991 to modern multiplexed, encrypted communication
Series Context: This is Part 6 of 20 in the Complete Protocols Master series. We're now at the Application Layer, exploring how web browsers and servers communicate.
# HTTP Request Structure
POST /api/users HTTP/1.1 ← Request Line (Method URI Version)
Host: api.example.com ← Required in HTTP/1.1
Content-Type: application/json ← Headers
Content-Length: 45
Authorization: Bearer eyJhbGc...
User-Agent: Mozilla/5.0
Accept: application/json
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
← Empty line (CRLF)
{"name": "Alice", "email": "a@b.com"} ← Body (optional)
# HTTP Response Structure
HTTP/1.1 201 Created ← Status Line (Version Code Reason)
Content-Type: application/json ← Headers
Content-Length: 67
Date: Mon, 31 Jan 2026 12:00:00 GMT
Server: nginx/1.24.0
Cache-Control: no-cache
Set-Cookie: session=abc123; HttpOnly
← Empty line
{"id": 123, "name": "Alice", ...} ← Body
HTTP Methods (Verbs)
Methods
HTTP Request Methods
Method
Purpose
Safe
Idempotent
Body
GET
Retrieve resource
✅
✅
❌
HEAD
GET without body (check headers)
✅
✅
❌
POST
Create resource / submit data
❌
❌
✅
PUT
Replace entire resource
❌
✅
✅
PATCH
Partial update
❌
❌
✅
DELETE
Remove resource
❌
✅
Optional
OPTIONS
Get allowed methods (CORS preflight)
✅
✅
❌
Safe: Doesn't modify server state (read-only) Idempotent: Same result regardless of how many times called
Important HTTP Headers
Headers
Common HTTP Headers Reference
# Request Headers (Client → Server)
Host: example.com # Required: Target server
User-Agent: Mozilla/5.0... # Client identification
Accept: text/html, application/json # Acceptable response types
Accept-Language: en-US,en;q=0.9 # Preferred languages
Accept-Encoding: gzip, deflate, br # Supported compression
Authorization: Bearer <token> # Authentication credentials
Cookie: session=abc123 # Stored cookies
Content-Type: application/json # Request body format
Content-Length: 1234 # Body size in bytes
Origin: https://app.example.com # CORS: request origin
Referer: https://example.com/page # Previous page URL
If-None-Match: "abc123" # Conditional: ETag check
If-Modified-Since: Wed, 21 Oct... # Conditional: freshness check
# Response Headers (Server → Client)
Content-Type: text/html; charset=utf-8 # Response body type
Content-Length: 5678 # Response body size
Content-Encoding: gzip # Applied compression
Date: Mon, 31 Jan 2026 12:00:00 GMT # Response timestamp
Server: nginx/1.24.0 # Server software
Set-Cookie: session=xyz; HttpOnly; Secure # Set cookie
Cache-Control: max-age=3600, public # Caching directives
ETag: "abc123" # Resource version tag
Last-Modified: Wed, 21 Oct 2025... # Last change time
Location: /new-url # Redirect target
Access-Control-Allow-Origin: * # CORS permissions
Strict-Transport-Security: max-age=... # HSTS
X-Content-Type-Options: nosniff # Security header
X-Frame-Options: DENY # Clickjacking protection
HTTP Status Codes
Status Codes
Status Code Categories
HTTP Status Codes:
1xx Informational (rare in practice)
├── 100 Continue # Keep sending request body
├── 101 Switching Protocols # WebSocket upgrade
└── 103 Early Hints # Preload resources (HTTP/2+)
2xx Success
├── 200 OK # Standard success
├── 201 Created # Resource created (POST)
├── 204 No Content # Success, no body (DELETE)
└── 206 Partial Content # Range request (video streaming)
3xx Redirection
├── 301 Moved Permanently # URL changed forever (SEO)
├── 302 Found # Temporary redirect (legacy)
├── 304 Not Modified # Use cached version
├── 307 Temporary Redirect # Keep method (POST stays POST)
└── 308 Permanent Redirect # Keep method, permanent
4xx Client Error
├── 400 Bad Request # Malformed request
├── 401 Unauthorized # Auth required (login)
├── 403 Forbidden # No permission (logged in but denied)
├── 404 Not Found # Resource doesn't exist
├── 405 Method Not Allowed # GET instead of POST, etc.
├── 408 Request Timeout # Client too slow
├── 409 Conflict # State conflict (duplicate)
├── 413 Payload Too Large # Body exceeds limit
├── 415 Unsupported Media Type # Wrong Content-Type
├── 422 Unprocessable Entity # Validation error (WebDAV but common)
├── 429 Too Many Requests # Rate limited
└── 451 Unavailable For Legal # Censorship/legal
5xx Server Error
├── 500 Internal Server Error # Generic server crash
├── 501 Not Implemented # Feature not available
├── 502 Bad Gateway # Upstream server error
├── 503 Service Unavailable # Maintenance/overload
└── 504 Gateway Timeout # Upstream timeout
HTTP Caching
Caching is crucial for web performance. Properly configured caching can reduce server load by 90% and dramatically improve page load times.
HTTP caching layers: browser cache, CDN edge cache, and origin server work together to reduce latency
Cache-Control
Cache-Control Directives
Cache-Control Header Directives:
# Cacheability
public # Any cache can store (CDN, browser)
private # Only browser cache (user-specific data)
no-cache # Must revalidate before using cached copy
no-store # Don't cache at all (sensitive data)
# Expiration
max-age=3600 # Fresh for 3600 seconds (1 hour)
s-maxage=86400 # Shared cache (CDN) max age
max-stale=600 # Accept stale responses up to 600s
min-fresh=60 # Response must be fresh for at least 60s
# Revalidation
must-revalidate # Must check origin when stale
proxy-revalidate # Shared caches must revalidate
immutable # Resource will never change (versioned URLs)
# Examples for different content types:
# Static assets (versioned filename: app.a1b2c3.js)
Cache-Control: public, max-age=31536000, immutable
# HTML pages (always revalidate)
Cache-Control: no-cache
# API responses (private, short cache)
Cache-Control: private, max-age=60
# Sensitive data (no caching)
Cache-Control: no-store
# CDN edge caching
Cache-Control: public, max-age=3600, s-maxage=86400
ETags and Conditional Requests
Validation
Cache Validation Flow
ETag-based Validation:
Initial Request:
Client → GET /data.json
Server → 200 OK
ETag: "abc123"
Cache-Control: max-age=60
[data]
After 60 seconds (stale):
Client → GET /data.json
If-None-Match: "abc123"
If unchanged:
Server → 304 Not Modified
(No body - use cached version)
If changed:
Server → 200 OK
ETag: "def456"
[new data]
Last-Modified Validation:
Initial Request:
Server → Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
Subsequent Request:
Client → If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
Server → 304 Not Modified OR 200 OK with new content
Best Practice:
- Use ETags for dynamic content (hashed content)
- Use Last-Modified for static files
- Combine both for maximum compatibility
# HTTP caching demonstration
import hashlib
from datetime import datetime, timedelta
def generate_etag(content: bytes) -> str:
"""Generate ETag from content hash"""
return f'"{hashlib.md5(content).hexdigest()}"'
def check_conditional_request(request_etag, current_etag):
"""Check if cached version is still valid"""
if request_etag == current_etag:
return {"status": 304, "body": None, "message": "Not Modified"}
return {"status": 200, "body": "new content", "message": "OK"}
# Simulate caching
content = b"Hello, World!"
etag = generate_etag(content)
print("HTTP Caching Simulation")
print("=" * 50)
print(f"Content: {content.decode()}")
print(f"Generated ETag: {etag}")
print()
# Simulate conditional request (content unchanged)
result = check_conditional_request(etag, etag)
print(f"Request with matching ETag:")
print(f" Status: {result['status']} {result['message']}")
print(f" Body: {result['body'] or '(use cached)'}")
print()
# Simulate content change
new_content = b"Hello, Updated World!"
new_etag = generate_etag(new_content)
result = check_conditional_request(etag, new_etag)
print(f"Request after content change:")
print(f" Status: {result['status']} {result['message']}")
print(f" New ETag: {new_etag}")
HTTP/2: Multiplexing Revolution
HTTP/2 (2015) addressed the performance limitations of HTTP/1.1 while maintaining backward compatibility. The key innovation: multiplexing multiple requests over a single TCP connection.
HTTP/2 multiplexing: multiple parallel streams over a single TCP connection eliminate head-of-line blocking
HTTP/2 Limitations: Despite multiplexing, HTTP/2 still suffers from TCP head-of-line blocking. If a packet is lost, all streams are blocked until TCP retransmits. This is why HTTP/3 moved to QUIC.
HTTP/3 and QUIC
HTTP/3 (2022) represents a fundamental shift: it runs over QUIC instead of TCP. This solves HTTP/2's remaining performance issues and enables new optimizations.
HTTP/3 protocol stack: QUIC replaces TCP, integrating TLS 1.3 encryption and running over UDP
Connection Establishment:
TCP + TLS 1.3 (HTTP/2):
Round 1: TCP SYN → SYN-ACK → ACK
Round 2: TLS ClientHello → ServerHello
Round 3: TLS Finished
Total: 2-3 RTT before first data
QUIC (HTTP/3) First Connection:
Round 1: QUIC Initial (includes TLS) → Response
Total: 1 RTT
QUIC Resumed Connection (0-RTT):
Round 0: QUIC 0-RTT + Application Data
Total: 0 RTT! (data sent immediately)
Head-of-Line Blocking Solved:
HTTP/2 over TCP:
┌─────────────────────────────────────────────────────┐
│ Stream 1: ■■■■■[PACKET LOST]─────────blocked────── │
│ Stream 3: ■■■■■─────────────blocked─────────────── │
│ Stream 5: ■■■■■─────────────blocked─────────────── │
│ │ │
│ All streams wait for TCP retransmit │
└─────────────────────────────────────────────────────┘
HTTP/3 over QUIC:
┌─────────────────────────────────────────────────────┐
│ Stream 1: ■■■■■[LOST]──────■■■■ (retransmit) │
│ Stream 3: ■■■■■■■■■■■■■■■■ (unaffected) │
│ Stream 5: ■■■■■■■■■■■■■■■■ (unaffected) │
│ │
│ Only affected stream is blocked │
└─────────────────────────────────────────────────────┘
Connection Migration:
TCP: IP address change = connection reset
QUIC: Connection ID survives network changes
Example: User walks from WiFi to cellular
- TCP: All connections dropped, re-establish
- QUIC: Seamless migration, no interruption
# Check HTTP/3 support
curl --http3 -I https://www.google.com 2>/dev/null
# Note: Requires curl 7.66+ compiled with HTTP/3 support
# Check via browser DevTools:
# Network tab → Protocol column shows "h3"
# Alternative: Use online tools
# https://http3check.net/
# https://www.cloudflare.com/learning/performance/what-is-http3/
# Wireshark: Filter QUIC traffic
# Filter: quic
# Check QUIC with quiche-client (Cloudflare)
# https://github.com/cloudflare/quiche
./quiche-client https://cloudflare-quic.com
HTTP/3 Adoption (2024): ~30% of top websites support HTTP/3. Major adopters include Google, Facebook, Cloudflare, and Fastly. Browser support is universal in Chrome, Firefox, Safari, and Edge.
WebSockets: Bidirectional Communication
WebSockets provide full-duplex communication over a single TCP connection. Unlike HTTP's request-response model, either side can send messages at any time.
WebSocket upgrade: HTTP connection upgrades to persistent full-duplex bidirectional communication
Handshake
WebSocket Upgrade Handshake
WebSocket Connection Establishment:
1. Client initiates HTTP upgrade request:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
2. Server accepts upgrade:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
3. Connection switches from HTTP to WebSocket protocol
════════════════════════════════════════════════
Full-duplex binary/text messages in both directions
No HTTP overhead per message
Connection stays open indefinitely
Key Generation:
Sec-WebSocket-Accept = Base64(SHA1(
Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
))
SSE provides a simpler alternative to WebSockets for server-to-client streaming. It uses standard HTTP, works through proxies, and auto-reconnects.
SSE vs WebSocket
Comparison: SSE vs WebSocket
Feature
Server-Sent Events
WebSockets
Direction
Server → Client only
Bidirectional
Protocol
HTTP
WS/WSS
Reconnection
Automatic
Manual
Data Format
Text only
Text + Binary
Browser Support
All modern (not IE)
All modern
Use Case
Notifications, feeds
Chat, games, real-time
# SSE Server Response Format
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: First message
data: Message with
data: multiple lines
event: userLogin
data: {"userId": 123, "name": "Alice"}
id: 42
event: notification
data: You have a new message!
retry: 5000
# Event format:
# event: (optional, default is "message")
# id: (optional, for reconnection)
# data: (required, can be multi-line)
# retry: (optional, reconnect interval)
# (blank line ends each event)