Topic Overview

HTTP/1.1 vs HTTP/2 vs HTTP/3

Compare HTTP versions: HTTP/1.1 (persistent connections), HTTP/2 (multiplexing, header compression), and HTTP/3 (QUIC, UDP-based).

HTTP has evolved through multiple versions, each addressing performance and security limitations of previous versions. Understanding the differences is crucial for building modern web applications.


HTTP/1.1

Released: 1997 Transport: TCP Key Features: Persistent connections, pipelining (limited)

Characteristics

  • One request per connection (without pipelining)
  • Text-based protocol: Human-readable headers
  • No header compression: Headers sent in full
  • Head-of-line blocking: One slow request blocks others
  • Multiple connections: Browsers open 6-8 connections per domain

Limitations

Connection 1: Request 1 → Response 1
Connection 2: Request 2 → Response 2
Connection 3: Request 3 → Response 3
...
Connection 6: Request 6 → Response 6

Problem: Need multiple TCP connections for parallelism

Head-of-line blocking:

Request 1 (slow) → Blocks Request 2, 3, 4...

HTTP/2

Released: 2015 Transport: TCP Key Features: Multiplexing, header compression, server push

Key Improvements

  1. Multiplexing: Multiple requests over single connection
  2. Header Compression (HPACK): Compress headers
  3. Binary Protocol: More efficient than text
  4. Server Push: Server can push resources proactively
  5. Stream Prioritization: Prioritize important resources

Multiplexing

Single TCP Connection:
  Stream 1: Request 1 → Response 1
  Stream 2: Request 2 → Response 2
  Stream 3: Request 3 → Response 3
  Stream 4: Request 4 → Response 4

All streams multiplexed over one connection

Benefits:

  • Fewer TCP connections
  • Better resource utilization
  • Reduced latency

Header Compression (HPACK)

HTTP/1.1:
  GET /api/users HTTP/1.1
  Host: example.com
  User-Agent: Mozilla/5.0...
  Accept: application/json
  Cookie: session=abc123...
  (Headers sent in full every time)

HTTP/2:
  (Headers compressed using HPACK)
  (Static table + dynamic table)
  (Much smaller header size)

Limitations

  • Still uses TCP: Head-of-line blocking at TCP level
  • TCP handshake: Still requires 3-way handshake
  • TCP congestion control: Can cause delays

HTTP/3

Released: 2022 Transport: QUIC (UDP-based) Key Features: QUIC protocol, connection migration, improved multiplexing

Key Improvements

  1. QUIC Protocol: UDP-based, eliminates TCP head-of-line blocking
  2. Built-in encryption: TLS 1.3 integrated
  3. Connection migration: Survives IP changes
  4. Faster handshake: 0-RTT or 1-RTT (vs 2-RTT for TCP+TLS)
  5. Independent streams: Stream blocking doesn't affect others

QUIC Protocol

HTTP/3 over QUIC (UDP):
  - Multiple streams over single QUIC connection
  - Each stream independent (no head-of-line blocking)
  - Built-in encryption (TLS 1.3)
  - Connection migration (survives IP changes)

Benefits:

  • No TCP head-of-line blocking: Streams are independent
  • Faster connection: 0-RTT or 1-RTT handshake
  • Better mobile: Handles network changes better

Comparison

FeatureHTTP/1.1HTTP/2HTTP/3
TransportTCPTCPQUIC (UDP)
MultiplexingNo (needs multiple connections)Yes (single connection)Yes (single connection)
Header CompressionNoYes (HPACK)Yes (QPACK)
Head-of-line BlockingYes (application level)Yes (TCP level)No (streams independent)
Server PushNoYesNo (replaced by Early Hints)
EncryptionOptional (HTTPS)Optional (HTTPS)Required (built-in)
Connection MigrationNoNoYes
HandshakeTCP (3-RTT) + TLS (2-RTT) = 5-RTTTCP (3-RTT) + TLS (2-RTT) = 5-RTTQUIC (0-RTT or 1-RTT)

Examples

HTTP/1.1 Request

GET /api/users HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Accept-Language: en-US,en;q=0.9
Cookie: session=abc123; theme=dark
Connection: keep-alive

Characteristics:

  • Text-based, human-readable
  • Headers sent in full
  • One request per connection (without pipelining)

HTTP/2 Request (Binary)

HTTP/2 uses binary frames:

HEADERS Frame:
  Stream ID: 1
  Flags: END_HEADERS
  Headers: (compressed with HPACK)
    :method: GET
    :path: /api/users
    :scheme: https
    :authority: example.com
    user-agent: Mozilla/5.0...
    accept: application/json

Characteristics:

  • Binary protocol (more efficient)
  • Headers compressed (HPACK)
  • Multiple streams over single connection

HTTP/3 Request (QUIC)

HTTP/3 over QUIC:

QUIC Packet:
  Connection ID: 0x1234
  Stream ID: 1
  Data: (HTTP/3 request)
    :method: GET
    :path: /api/users
    :scheme: https
    :authority: example.com

Encrypted with TLS 1.3 (built-in)

Characteristics:

  • UDP-based (QUIC)
  • Built-in encryption
  • Independent streams

Performance Comparison

import time
import requests

def measure_http_performance(url, version='1.1'):
    """Measure HTTP performance"""
    start = time.time()
    
    if version == '1.1':
        # HTTP/1.1: Multiple connections
        with requests.Session() as session:
            for i in range(10):
                session.get(f"{url}/resource{i}")
    
    elif version == '2':
        # HTTP/2: Single connection, multiplexing
        import httpx
        with httpx.Client(http2=True) as client:
            for i in range(10):
                client.get(f"{url}/resource{i}")
    
    elif version == '3':
        # HTTP/3: QUIC
        import httpx
        with httpx.Client(http3=True) as client:
            for i in range(10):
                client.get(f"{url}/resource{i}")
    
    elapsed = time.time() - start
    return elapsed

# Results (typical):
# HTTP/1.1: ~2.0s (multiple connections)
# HTTP/2:   ~1.2s (multiplexing)
# HTTP/3:   ~0.8s (QUIC, no TCP blocking)

Migration Strategy

HTTP/1.1 → HTTP/2

Benefits:

  • Multiplexing (single connection)
  • Header compression
  • Server push (optional)

Considerations:

  • Requires HTTPS (most browsers)
  • Server must support HTTP/2
  • Still has TCP head-of-line blocking

HTTP/2 → HTTP/3

Benefits:

  • No TCP head-of-line blocking
  • Faster handshake (0-RTT or 1-RTT)
  • Better mobile performance
  • Connection migration

Considerations:

  • Requires QUIC support
  • UDP may be blocked by some firewalls
  • Newer protocol (less widespread support)

Common Pitfalls

  • Not using HTTPS: HTTP/2 requires HTTPS in most browsers. Fix: Always use HTTPS
  • Ignoring HTTP/2 server push: Not utilizing server push. Fix: Use server push for critical resources
  • Not optimizing for HTTP/2: Still using HTTP/1.1 optimizations (domain sharding). Fix: Remove unnecessary optimizations
  • Firewall blocking UDP: HTTP/3 uses UDP, may be blocked. Fix: Ensure UDP port 443 is open
  • Not monitoring performance: Not measuring actual improvements. Fix: Monitor metrics, A/B test
  • Assuming HTTP/3 is always faster: Depends on network conditions. Fix: Test in your environment

Interview Questions

Beginner

Q: What are the main differences between HTTP/1.1, HTTP/2, and HTTP/3?

A:

HTTP/1.1 (1997):

  • Transport: TCP
  • Multiplexing: No (needs multiple connections)
  • Header compression: No
  • Limitations: Head-of-line blocking, text-based protocol

HTTP/2 (2015):

  • Transport: TCP
  • Multiplexing: Yes (single connection, multiple streams)
  • Header compression: Yes (HPACK)
  • Improvements: Binary protocol, server push, stream prioritization
  • Limitation: Still has TCP head-of-line blocking

HTTP/3 (2022):

  • Transport: QUIC (UDP-based)
  • Multiplexing: Yes (single connection, independent streams)
  • Header compression: Yes (QPACK)
  • Improvements: No TCP blocking, built-in encryption, connection migration
  • Benefits: Faster handshake, better mobile performance

Key Differences:

  • HTTP/1.1: Multiple connections, text-based
  • HTTP/2: Single connection, multiplexing, binary
  • HTTP/3: QUIC, UDP-based, no TCP blocking

Intermediate

Q: Explain how HTTP/2 multiplexing works and why it's better than HTTP/1.1.

A:

HTTP/1.1 Problem:

Connection 1: Request 1 → (wait) → Response 1
Connection 2: Request 2 → (wait) → Response 2
Connection 3: Request 3 → (wait) → Response 3
...
Connection 6: Request 6 → (wait) → Response 6

Issues:
- Need 6 TCP connections
- Each connection has overhead
- Head-of-line blocking (slow request blocks connection)

HTTP/2 Solution:

Single TCP Connection:
  Stream 1: Request 1 → Response 1
  Stream 2: Request 2 → Response 2
  Stream 3: Request 3 → Response 3
  Stream 4: Request 4 → Response 4
  Stream 5: Request 5 → Response 5
  Stream 6: Request 6 → Response 6

All streams multiplexed over one connection

How it works:

  1. Single TCP connection: All requests use one connection
  2. Streams: Each request/response is a stream with unique ID
  3. Frames: Data sent in binary frames (HEADERS, DATA, etc.)
  4. Interleaving: Frames from different streams interleaved
  5. Reassembly: Receiver reassembles frames by stream ID

Benefits:

  • Fewer connections: One connection vs multiple
  • Better resource utilization: Single connection more efficient
  • Reduced latency: No connection establishment overhead
  • Header compression: HPACK reduces header size

Example:

HTTP/1.1: 6 connections × 3-way handshake = 18 packets
HTTP/2:   1 connection × 3-way handshake = 3 packets

Senior

Q: Design a web application that supports HTTP/1.1, HTTP/2, and HTTP/3. How do you handle protocol negotiation, fallback, and optimize for each version?

A:

class MultiProtocolWebServer {
  private http1Server: HTTP1Server;
  private http2Server: HTTP2Server;
  private http3Server: HTTP3Server;
  private protocolNegotiator: ProtocolNegotiator;
  
  constructor() {
    this.http1Server = new HTTP1Server({ port: 80 });
    this.http2Server = new HTTP2Server({ port: 443 });
    this.http3Server = new HTTP3Server({ port: 443 });
    this.protocolNegotiator = new ProtocolNegotiator();
  }
  
  // 1. Protocol Negotiation
  async handleConnection(connection: Connection): Promise<void> {
    // ALPN (Application-Layer Protocol Negotiation)
    const supportedProtocols = await connection.getALPN();
    
    if (supportedProtocols.includes('h3')) {
      // HTTP/3 (QUIC)
      return await this.http3Server.handle(connection);
    } else if (supportedProtocols.includes('h2')) {
      // HTTP/2
      return await this.http2Server.handle(connection);
    } else {
      // HTTP/1.1 (fallback)
      return await this.http1Server.handle(connection);
    }
  }
  
  // 2. HTTP/1.1 Optimization
  class HTTP1Server {
    async handleRequest(request: Request): Promise<Response> {
      // Domain sharding (if needed)
      // Connection keep-alive
      // Resource bundling
      return await this.processRequest(request);
    }
  }
  
  // 3. HTTP/2 Optimization
  class HTTP2Server {
    async handleRequest(request: Request): Promise<Response> {
      // Multiplexing (automatic)
      // Header compression (HPACK)
      // Server push for critical resources
      
      // Push critical resources
      if (request.path === '/') {
        await this.pushResource('/styles.css');
        await this.pushResource('/app.js');
      }
      
      return await this.processRequest(request);
    }
    
    async pushResource(path: string): Promise<void> {
      // Server push
      await this.stream.pushPromise({
        ':path': path,
        ':method': 'GET'
      });
    }
  }
  
  // 4. HTTP/3 Optimization
  class HTTP3Server {
    async handleRequest(request: Request): Promise<Response> {
      // QUIC (UDP-based)
      // Independent streams (no blocking)
      // Built-in encryption
      // Connection migration
      
      return await this.processRequest(request);
    }
  }
  
  // 5. Fallback Strategy
  class ProtocolNegotiator {
    async negotiate(clientCapabilities: ClientCapabilities): Promise<Protocol> {
      // Try HTTP/3 first
      if (clientCapabilities.supportsHTTP3 && this.supportsHTTP3()) {
        return 'HTTP/3';
      }
      
      // Fallback to HTTP/2
      if (clientCapabilities.supportsHTTP2 && this.supportsHTTP2()) {
        return 'HTTP/2';
      }
      
      // Fallback to HTTP/1.1
      return 'HTTP/1.1';
    }
  }
  
  // 6. Performance Monitoring
  async getMetrics(): Promise<Metrics> {
    return {
      http1: {
        connections: await this.http1Server.getConnectionCount(),
        avgLatency: await this.http1Server.getAvgLatency()
      },
      http2: {
        streams: await this.http2Server.getStreamCount(),
        avgLatency: await this.http2Server.getAvgLatency(),
        pushCount: await this.http2Server.getPushCount()
      },
      http3: {
        connections: await this.http3Server.getConnectionCount(),
        avgLatency: await this.http3Server.getAvgLatency(),
        migrationCount: await this.http3Server.getMigrationCount()
      }
    };
  }
}

Features:

  1. Protocol negotiation: ALPN for automatic selection
  2. Fallback strategy: HTTP/3 → HTTP/2 → HTTP/1.1
  3. Optimization per version: Different strategies for each
  4. Server push: HTTP/2 server push for critical resources
  5. Monitoring: Track performance per protocol
  6. Graceful degradation: Fallback to older versions

Key Takeaways

  • HTTP/1.1: Multiple connections, text-based, no multiplexing, head-of-line blocking
  • HTTP/2: Single connection, multiplexing, header compression, binary protocol, still has TCP blocking
  • HTTP/3: QUIC (UDP), independent streams, no TCP blocking, built-in encryption, connection migration
  • Multiplexing: HTTP/2 allows multiple requests over single connection
  • Header compression: HPACK (HTTP/2), QPACK (HTTP/3) reduce header size
  • Protocol negotiation: ALPN for automatic protocol selection
  • Migration strategy: Support multiple versions, fallback gracefully
  • Best practices: Use HTTPS, optimize for each version, monitor performance

About the author

InterviewCrafted helps you master system design with patience. We believe in curiosity-led engineering, reflective writing, and designing systems that make future changes feel calm.