HTTP & Web Protocols

From HTTP/1.0 to HTTP/3, WebSockets, and Beyond

HTTP Evolution

HTTP Timeline

HTTP/1.0

  • One request per connection
  • No keep-alive (default)
  • New TCP handshake for each request
  • Slow and inefficient
GET /index.html HTTP/1.0
[connection closes]

GET /style.css HTTP/1.0
[new connection, new handshake]

HTTP/1.1

  • Persistent connections (keep-alive)
  • Pipelining (sequential)
  • Chunked transfer encoding
  • Host header (virtual hosting)
GET /index.html HTTP/1.1
Host: example.com
Connection: keep-alive

GET /style.css HTTP/1.1
[same connection reused]

Problem: Head-of-line blocking - responses must be in order

HTTP/2

  • Binary protocol (not text)
  • Multiplexing: Multiple streams over one connection
  • Server push: Send resources before requested
  • Header compression (HPACK)
  • Stream prioritization
One TCP connection:
Stream 1: /index.html
Stream 2: /style.css (parallel!)
Stream 3: /script.js (parallel!)

Still has TCP-level head-of-line blocking

HTTP/3

  • QUIC protocol (UDP-based)
  • No TCP head-of-line blocking
  • Faster connection setup (0-RTT)
  • Better mobile performance
  • Built-in encryption (TLS 1.3)
UDP-based QUIC:
- 0-RTT connection resumption
- Stream-level error recovery
- Connection migration (IP change)

Key Differences Summary

Feature HTTP/1.1 HTTP/2 HTTP/3
Protocol Text over TCP Binary over TCP Binary over QUIC (UDP)
Connections 6 parallel per domain 1 connection, multiplexed 1 QUIC connection
Head-of-line blocking Yes (HTTP level) No (HTTP), Yes (TCP) No
Header compression No HPACK QPACK
Server push No Yes Yes (rarely used)
TLS Optional De facto required Built-in (TLS 1.3)
Connection Setup TCP 3-way + TLS (2-3 RTT) TCP 3-way + TLS (2-3 RTT) 0-1 RTT

HTTP Request/Response Anatomy

HTTP Request Structure

POST /api/users HTTP/1.1                    ← Request Line
Host: api.example.com                       ← Headers
Content-Type: application/json
Authorization: Bearer eyJhbGc...
Content-Length: 58
User-Agent: Mozilla/5.0
Accept: application/json
Connection: keep-alive
                                             ← Blank line
{"name": "Alice", "email": "alice@example.com"}  ← Body

Request Line Components

HTTP Methods

Method Purpose Idempotent Safe Cacheable
GET Retrieve resource Yes Yes Yes
POST Create resource No No Rarely
PUT Replace resource Yes No No
PATCH Partial update No No No
DELETE Remove resource Yes No No
HEAD GET without body (headers only) Yes Yes Yes
OPTIONS Get allowed methods (CORS preflight) Yes Yes No

Idempotent: Multiple identical requests have same effect as one

Safe: Read-only, doesn't modify server state

Example: DELETE /users/123 is idempotent - deleting same user 5 times = same result (user deleted)

HTTP Response Structure

HTTP/1.1 200 OK                              ← Status Line
Date: Mon, 01 Jan 2024 12:00:00 GMT         ← Headers
Server: nginx/1.18.0
Content-Type: application/json; charset=utf-8
Content-Length: 134
Cache-Control: max-age=3600
Set-Cookie: session=abc123; HttpOnly; Secure
Access-Control-Allow-Origin: *
                                             ← Blank line
{"id": 42, "name": "Alice", ...}            ← Body

HTTP Status Codes

Code Meaning Use Case
1xx - Informational
100 Continue Client should continue with request body
101 Switching Protocols Upgrade to WebSocket
2xx - Success
200 OK Request succeeded
201 Created Resource created (POST, PUT)
204 No Content Success, but no body (DELETE)
3xx - Redirection
301 Moved Permanently Resource permanently moved, update bookmarks
302 Found Temporary redirect
304 Not Modified Use cached version (conditional GET)
4xx - Client Error
400 Bad Request Malformed syntax, invalid JSON
401 Unauthorized Authentication required or failed
403 Forbidden Authenticated but not authorized
404 Not Found Resource doesn't exist
429 Too Many Requests Rate limit exceeded
5xx - Server Error
500 Internal Server Error Generic server error
502 Bad Gateway Invalid response from upstream server
503 Service Unavailable Server overloaded or down
504 Gateway Timeout Upstream server timeout

HTTP Headers

Common Request Headers

Header Purpose Example
Host Domain name (required in HTTP/1.1) Host: api.example.com
User-Agent Client software identifier User-Agent: Mozilla/5.0 ...
Accept Acceptable response formats Accept: application/json
Authorization Authentication credentials Authorization: Bearer token123
Content-Type Request body format Content-Type: application/json
Cookie Send cookies to server Cookie: session=abc123
If-None-Match Conditional request (ETag) If-None-Match: "abc123"
If-Modified-Since Conditional request (timestamp) If-Modified-Since: Mon, 01 Jan 2024
Origin CORS - request origin Origin: https://example.com

Common Response Headers

Header Purpose Example
Content-Type Response body format Content-Type: text/html; charset=utf-8
Content-Length Body size in bytes Content-Length: 1234
Set-Cookie Set cookie in client Set-Cookie: session=abc; HttpOnly
Location Redirect target (3xx status) Location: /new-url
ETag Resource version identifier ETag: "33a64df551425fcc55e"
Last-Modified Resource modification time Last-Modified: Mon, 01 Jan 2024

Caching Headers

Cache-Control Directives

Directive Meaning
no-cache Cache, but revalidate with server before using
no-store Don't cache at all (sensitive data)
max-age=3600 Cache for 3600 seconds (1 hour)
public Can be cached by any cache (CDN, browser)
private Only browser cache, not shared caches
must-revalidate Must check with server when stale
immutable Never changes (perfect for hashed assets)
Examples:
# Static assets (versioned filenames)
Cache-Control: public, max-age=31536000, immutable
# Cache for 1 year, never revalidate

# API responses
Cache-Control: private, max-age=300
# Cache in browser for 5 minutes

# Sensitive data
Cache-Control: no-store
# Don't cache

Conditional Requests (304 Not Modified)

# Initial request
GET /api/users/123 HTTP/1.1

# Response
HTTP/1.1 200 OK
ETag: "v1.23.45"
Last-Modified: Mon, 01 Jan 2024 12:00:00 GMT
{ "id": 123, "name": "Alice" }

# Subsequent request
GET /api/users/123 HTTP/1.1
If-None-Match: "v1.23.45"

# Response (resource unchanged)
HTTP/1.1 304 Not Modified
ETag: "v1.23.45"
# No body - use cached version

Security Headers

Header Purpose Example
Strict-Transport-Security (HSTS) Force HTTPS for specified time Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy (CSP) Prevent XSS by restricting resource sources Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com
X-Frame-Options Prevent clickjacking (iframe embedding) X-Frame-Options: DENY or SAMEORIGIN
X-Content-Type-Options Prevent MIME-type sniffing X-Content-Type-Options: nosniff
Referrer-Policy Control Referer header information Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy Control browser features (camera, geolocation) Permissions-Policy: geolocation=(), camera=()

CORS Headers

Cross-Origin Resource Sharing (CORS) - Allows controlled access to resources from different origins.
# Preflight request (OPTIONS)
OPTIONS /api/data HTTP/1.1
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type

# Preflight response
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400  # Cache preflight for 24 hours

# Actual request
POST /api/data HTTP/1.1
Origin: https://example.com
Content-Type: application/json

# Actual response
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

HTTPS & TLS

TLS Handshake (TLS 1.2)

Client                                  Server
  |                                         |
  | 1. ClientHello                          |
  |   - TLS versions supported              |
  |   - Cipher suites supported             |
  |   - Random number                       |
  |---------------------------------------->|
  |                                         |
  |                   2. ServerHello        |
  |                      - TLS version      |
  |                      - Cipher suite     |
  |                      - Random number    |
  |                   3. Certificate        |
  |                      - Server cert      |
  |                   4. ServerHelloDone    |
  |<----------------------------------------|
  |                                         |
  | 5. ClientKeyExchange                    |
  |   - Pre-master secret (encrypted)       |
  | 6. ChangeCipherSpec                     |
  | 7. Finished (encrypted)                 |
  |---------------------------------------->|
  |                                         |
  |                8. ChangeCipherSpec      |
  |                9. Finished (encrypted)  |
  |<----------------------------------------|
  |                                         |
  | Application Data (encrypted)            |
  |<--------------------------------------->|

2 Round Trips (2-RTT) before application data can be sent

TLS 1.3 Handshake (Faster)

Client                                  Server
  |                                         |
  | ClientHello                             |
  |   + Key Share (DH public key)           |
  |---------------------------------------->|
  |                                         |
  |                   ServerHello           |
  |                   + Key Share           |
  |                   + Certificate         |
  |                   + Finished            |
  |<----------------------------------------|
  | Application Data (encrypted)            |
  |<--------------------------------------->|

1 Round Trip (1-RTT) - much faster!

0-RTT resumption: Reconnecting clients can send encrypted data immediately

Certificate Validation

  1. Server sends certificate chain:
    • example.com certificate
    • Intermediate CA certificate
    • Root CA certificate (optional, client has it)
  2. Client validates:
    • Certificate not expired
    • Domain matches (example.com)
    • Signature valid (signed by trusted CA)
    • Not revoked (OCSP, CRL)
  3. Trust chain: example.com ← Intermediate CA ← Root CA (trusted)

Real-Time Communication

1. Polling

Client repeatedly requests updates

Client → Server: GET /updates
Server → Client: {"new": false}
[wait 5 seconds]
Client → Server: GET /updates
Server → Client: {"new": false}
[wait 5 seconds]
Client → Server: GET /updates
Server → Client: {"new": true, "data": ...}

Pros: Simple, works everywhere

Cons: Wasteful, high latency

2. Long Polling

Server holds request until data available

Client → Server: GET /updates
[server waits...]
[new data arrives]
Server → Client: {"data": ...}
Client → Server: GET /updates [immediately]
[server waits...]

Pros: Lower latency than polling

Cons: Ties up connections, complex

3. Server-Sent Events (SSE)

Server pushes updates over persistent connection

Client → Server: GET /events
                  Accept: text/event-stream

Server → Client: (connection stays open)
data: {"message": "Hello"}

data: {"message": "Update 1"}

data: {"message": "Update 2"}

Pros: Simple, auto-reconnect, event IDs

Cons: One-way only (server→client)

JavaScript:
const eventSource = new EventSource('/events');

eventSource.onmessage = (event) => {
  console.log('New message:', event.data);
};

eventSource.onerror = () => {
  console.log('Connection lost, reconnecting...');
};

4. WebSockets

Full-duplex communication (bidirectional)

# Initial handshake (HTTP upgrade)
Client → Server:
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9...

Server → Client:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm...

# Now bidirectional frames
Client ↔ Server:
[binary frame: user typing...]
[text frame: "Hello"]
[binary frame: audio data...]

Pros: Full duplex, low overhead, binary support

Cons: More complex, needs special server support

JavaScript:
const ws = new WebSocket('wss://example.com/chat');

ws.onopen = () => {
  ws.send(JSON.stringify({ type: 'join', room: 'lobby' }));
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log('Received:', message);
};

ws.send(JSON.stringify({ type: 'message', text: 'Hello!' }));

When to Use What?

Technique Best For
Polling Infrequent updates, simple requirements
Long Polling Legacy browser support, moderate real-time needs
SSE One-way updates (server→client): notifications, live feeds, stock tickers
WebSockets Bidirectional real-time: chat, gaming, collaborative editing, live trading

Performance Optimizations

Compression

Algorithm Compression Ratio Speed Browser Support
gzip Good (60-80%) Fast All browsers
Brotli Better (70-85%) Slower compression, fast decompression Modern browsers (95%+)
Deflate Similar to gzip Fast All browsers (rarely used)
# Request
GET /script.js HTTP/1.1
Accept-Encoding: gzip, deflate, br

# Response
HTTP/1.1 200 OK
Content-Encoding: br
Content-Type: application/javascript
Content-Length: 15234  # Compressed size

[compressed data]
Best Practice:
  • Use Brotli for static assets (pre-compress at build time)
  • Use gzip for dynamic content (faster compression)
  • Don't compress already-compressed formats (images, videos)
  • Compression saves 60-80% bandwidth for text (HTML, CSS, JS, JSON)

HTTP/2 Multiplexing

HTTP/1.1 (6 Connections)

Connection 1: /index.html ████████
Connection 2: /style.css  ██████
Connection 3: /script.js  ████
Connection 4: /image1.png ██████████
Connection 5: /image2.png ████████
Connection 6: /image3.png ██████

# Max 6 parallel, then queue
Connection 1: /image4.png ██████
[waiting...]

HTTP/2 (1 Connection, Streams)

One TCP Connection:
Stream 1: /index.html  ████████
Stream 2: /style.css   ██████
Stream 3: /script.js   ████
Stream 4: /image1.png  ██████████
Stream 5: /image2.png  ████████
Stream 6: /image3.png  ██████
Stream 7: /image4.png  ██████
...unlimited streams...

Benefits:

HTTP/2 Server Push

Server proactively sends resources before client requests them

# Client requests HTML
GET /index.html HTTP/2

# Server pushes CSS and JS before client parses HTML
PUSH_PROMISE: /style.css
PUSH_PROMISE: /script.js

# Server sends responses
Stream 1: /index.html → <html><link href="/style.css">...
Stream 2: /style.css → body { ... } [pushed]
Stream 3: /script.js → console.log(...) [pushed]

Pros: Eliminates round trips for critical resources

Cons: Can push unnecessary resources, cache issues

Reality: Not widely used, being removed from Chrome

HTTP/3 & QUIC

Why HTTP/3?

Problem with TCP: Head-of-line blocking at TCP level

HTTP/2 over TCP:
Stream 1: ████ [packet lost!] [blocked]
Stream 2: ████ [waiting...] [blocked]
Stream 3: ████ [waiting...] [blocked]

# All streams blocked until lost packet retransmitted

HTTP/3 over QUIC (UDP):
Stream 1: ████ [packet lost!] [stream 1 blocked]
Stream 2: ████████ [continues!]
Stream 3: ████████ [continues!]

# Only affected stream blocked

QUIC Benefits

Connection Setup Time:
  • HTTP/1.1 + TLS: 3 RTT (TCP 3-way + TLS handshake + request)
  • HTTP/2 + TLS: 3 RTT (same as HTTP/1.1)
  • HTTP/3 + QUIC: 1 RTT (combined handshake)
  • HTTP/3 + QUIC (resumption): 0 RTT

Key Takeaways

Interview Tips