> ## Content Index
> Fetch the complete content index at: https://snubmonkey.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# NGINX: HTTP/2 vs HTTP/3 — What Actually Changes Under the Hood?
- URL: https://snubmonkey.com/nginx-http-2-vs-http-3-what-actually-changes-under-the-hood/
- Published: 2026-08-20T04:13:06.000Z
- Updated: 2026-08-20T04:13:07.000Z
- Description: HTTP/2 and HTTP/3 both improve web performance, but use fundamentally different transports. HTTP/2 runs over TCP, while HTTP/3 uses QUIC over UDP—changing connection handling, packet processing, streams, congestion control, and NGINX worker behavior.
- Author: yannick Coffi
- Tags: NGINX, how to 🪄, https, self-hosting 🪲

HTTP/2 and HTTP/3 were both designed to make the modern web faster, but they take fundamentally different approaches to transporting HTTP traffic.

If you run NGINX, the distinction is especially important because enabling HTTP/3 is not simply a matter of changing `http2` to `http3`. HTTP/2 runs over **TCP**, while HTTP/3 runs over **QUIC**, which runs over **UDP**.

That difference changes the connection model, packet handling, congestion control, stream behavior, and even how NGINX workers can process incoming traffic.

## **HTTP/2: HTTP Multiplexing Over TCP**

HTTP/1.1 traditionally required multiple connections or techniques such as pipelining to efficiently retrieve many resources.

HTTP/2 changed that model.

Instead of treating every request as an independent connection, HTTP/2 uses a single TCP connection containing multiple **streams, which are independent logical channels used to carry individual requests and responses concurrently**.

In essence:

```text
                    TCP CONNECTION
                         │
          ┌──────────────┼──────────────┐
          │              │              │
       Stream 1       Stream 3       Stream 5
          │              │              │
       HTML            CSS             JS
```

This is called **multiplexing**.  

A browser can request the HTML document, CSS, JavaScript, images, fonts, and other resources concurrently over the same connection.

NGINX therefore does not need a separate TCP connection for every HTTP/2 request.

A simplified NGINX configuration looks like:

```nginx
server {
    listen 443 ssl;
    listen [::]:443 ssl;

    http2 on;

    server_name example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    ...
}
```

The important part is:

```nginx
http2 on;
```

`http2 on;` enables HTTP/2 for the server block. It tells NGINX that clients connecting to that server over HTTPS may use HTTP/2 instead of HTTP/1.1, provided the client supports it and TLS negotiation selects HTTP/2.

The connection itself is still TCP.

```text
Client
   │
   │ HTTPS
   │ TCP
   ▼
NGINX :443
   │
   └── HTTP/2
       ├── Stream 1 → Request / Response
       ├── Stream 3 → Request / Response
       ├── Stream 5 → Request / Response
       └── Stream 7 → Request / Response
```

The **TCP connection remains the underlying transport**. HTTP/2 changes how HTTP messages are multiplexed over that connection.

## **What `http2 on;`actually changes**

Without HTTP/2:

```text
HTTPS
  │
  ▼
TCP connection
  │
  └── HTTP/1.1 requests
```

### **One important point**

`http2 on;` **does not mean “use HTTP/2 only.”**

A client that doesn’t support HTTP/2 can still connect using HTTP/1.1\. During the TLS handshake, **ALPN** is used to negotiate the protocol.

In essence:

```text
                 TLS + ALPN
                     │
             ┌───────┴───────┐
             ▼               ▼
         HTTP/2           HTTP/1.1
             │
       TCP connection
```

**HTTP/2 = TCP + HTTP/2 streams**

# **The HTTP/2 Problem: TCP Head-of-Line Blocking**

HTTP/2 solved a major problem with HTTP/1.1, but it inherited a limitation from TCP.

TCP presents applications with an **ordered** byte stream.

HTTP/2 solved a major problem with HTTP/1.1 by allowing multiple requests and responses to share a single connection. However, it inherited an important limitation from the TCP transport underneath it.

TCP presents applications with a single, ordered byte stream. Data can arrive out of order at the network level, but TCP must reassemble that data and deliver the byte stream to the application in order.

Consider an HTTP/2 connection carrying several independent streams:

```text
                 ONE TCP CONNECTION
══════════════════════════════════════════════════════►

   Stream A ────────[A1][A2][A3][A4]──────────────►
   Stream B ────────[B1][B2][B3][B4]──────────────►
   Stream C ────────[C1][C2][C3][C4]──────────────►
   Stream D ────────[D1][D2][D3][D4]──────────────►
                         ▲
                         │
                    TCP data lost
```

Suppose the TCP segment containing part of Stream A is lost. Data belonging to Streams B, C, and D may still reach the server successfully, but TCP cannot simply deliver bytes beyond the missing portion to the application while maintaining its required ordered byte stream.

The later data can be received and buffered, but the missing data must be recovered before TCP can provide a continuous stream to the application.

This creates **TCP-level head-of-line (HOL) blocking**.

The important point is that HTTP/2 streams are logically independent, but TCP does not know about those streams. From TCP’s perspective, everything is simply part of one ordered byte stream.

HTTP/2 therefore provides excellent multiplexing at the HTTP layer, but all of those streams ultimately depend on the same TCP connection.

**HTTP/3 takes a fundamentally different approach by moving stream multiplexing into QUIC, where streams are independently managed at the transport layer.**

# **HTTP/3: HTTP Over QUIC**

HTTP/3 does not run over TCP.

It runs over **QUIC**, and QUIC runs over UDP.

The stack becomes:

```text
HTTP/3
   │
   ▼
 QUIC
   │
   ▼
 UDP
   │
   ▼
 IP
```

Instead of:

```text
HTTP/2
   │
   ▼
 TLS
   │
   ▼
 TCP
   │
   ▼
 IP
```

  
This is the most important architectural difference between HTTP/2 and HTTP/3.  
  
**HTTP/2 relies on TCP for transport and uses TLS as a separate security layer, while HTTP/3 uses QUIC as its secure, multiplexed transport over UDP.**

  
# **QUIC Is More Than “UDP”**

It is tempting to describe HTTP/3 as simply “HTTP over UDP.”

That is technically true at a very low level, but it misses the important part.

UDP itself does not provide:

- reliable delivery
- ordering
- congestion control
- connection management
- retransmission
- encrypted transport

QUIC implements these capabilities above UDP. It provides a modern, encrypted transport layer while using UDP as its underlying datagram mechanism.

So the architecture is better understood as:

```text
┌──────────────────────────────┐
│           HTTP/3             │
│     HTTP semantics           │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│            QUIC              │
│  Streams                     │
│  Reliable delivery           │
│  Retransmission              │
│  Flow control                │
│  Congestion control          │
│  Connection management       │
│  TLS 1.3 integration         │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│             UDP              │
│       Datagram transport     │
└──────────────┬───────────────┘
               │
┌──────────────▼───────────────┐
│             IP               │
└──────────────────────────────┘
```

UDP provides the basic datagram transport. QUIC builds the sophisticated transport functionality on top of it, and HTTP/3 runs on top of QUIC.

That distinction is essential: **HTTP/3 is not “HTTP directly over UDP.” It is HTTP/3 over QUIC over UDP.**

# **HTTP/3 Streams Are Independent**

This is one of the most important differences between HTTP/2 and HTTP/3.

HTTP/3 uses **QUIC streams**, and each stream has its own ordered delivery. A packet loss affecting one stream does not necessarily block unrelated streams from continuing.

In essence:

```text
                    QUIC CONNECTION
                           │
             ┌─────────────┼─────────────┐
             │             │             │
          Stream 1      Stream 3      Stream 5
             │             │             │
           HTML           CSS            JS
             │             │             │
             ▼             X             ▼
          received      packet loss    received
                           │
                           │
                      retransmit
                           │
                           ▼
                       CSS data
```

Suppose a QUIC packet carrying part of the **CSS stream** is lost. QUIC can detect the loss and retransmit the missing data, while data belonging to the **HTML and JavaScript streams can continue to be delivered** if it has arrived successfully.

The browser therefore does not necessarily have to wait for the missing CSS data before receiving additional data from unrelated streams.

This is a major architectural advantage of QUIC: **loss on one stream does not inherently create transport-level head-of-line blocking for every other stream in the connection.**

That is fundamentally different from HTTP/2 over TCP, where all HTTP/2 streams share the same ordered TCP byte stream.

# **So, What’s the Fuzz?** 

Okay, we know how HTTP/3 works.   
**But what’s the big deal?**

Going deep into QUIC internals, packet recovery, congestion control, and connection migration is beyond the scope of this article. The important takeaway is that HTTP/3 gives the web a modern transport layer designed to reduce latency, handle packet loss more gracefully, and keep connections moving efficiently.

But this isn’t just theoretical.

## **What Websites Use QUIC?**

QUIC already powers a growing part of the modern web through HTTP/3\. Major platforms—including SNUBmonkey™, Google, YouTube, Facebook, and sites using CDNs like Cloudflare—support it.

**QUIC isn’t waiting for the future. The future is already using it.**

What matters is what HTTP/3 can actually do for real-world applications:

### **1\. Better on Bad Networks**

HTTP/3 handles packet loss more efficiently, so a problem with one stream doesn’t necessarily slow down the others.

### **2\. Better for Mobile**

Cellular networks can be unpredictable. HTTP/3 is designed to handle latency, packet loss, congestion, and changing network conditions more efficiently.

### **3\. Faster Connections**

QUIC combines transport setup and TLS 1.3, reducing the time needed to establish a secure connection.

### **4\. Survives Network Changes**

QUIC can keep a connection alive when a device switches networks, such as moving from Wi-Fi to cellular.

### **5\. Less Head-of-Line Blocking**

In HTTP/2, multiple requests share one TCP connection. If a packet is lost, TCP may hold up data that arrived after it—even if that data belongs to a different request.

### **6\. Great for Modern Apps**

APIs, streaming, large downloads, live updates, and other applications with multiple simultaneous data flows can benefit from QUIC’s stream architecture.

### **7\. HTTP/2 Still Has a Place**

HTTP/3 doesn’t replace HTTP/2 overnight. NGINX can serve both:

```text
TCP :443  → HTTP/2
UDP :443  → HTTP/3 / QUIC
```

Clients that support HTTP/3 can use QUIC, while everyone else can continue using HTTP/2.

### **The Point?**

**HTTP/3 isn’t simply “faster HTTP.”**

It’s about making applications **more responsive and resilient when the network isn’t perfect**.

That’s the real fuzz.

# 

## 

## 

**HTTP/3** isn’t about replacing **HTTP/2**—it’s about building a more resilient, modern transport for today’s web. The real benefits show up when networks are slow, lossy, congested, or constantly changing.

Thanks for reading—and as always, keep experimenting, breaking things safely, and learning.

See you on the next one.