What Does HTTP/3 Mean for NGINX?

HTTP/3 changes how NGINX handles web traffic. QUIC replaces TCP with UDP as its underlying transport, introducing a different connection and worker model while allowing HTTP/2 and HTTP/3 to run side by side.

What  Does HTTP/3 Mean for NGINX?
Photo by Alexander von Schulz / Unsplash

In our previous article, “NGINX: HTTP/2 vs HTTP/3 — What Actually Changes Under the Hood?”, we looked beneath the protocol versions to understand how HTTP/2 and HTTP/3 actually transport web traffic.

Now, let’s bring that down to the NGINX configuration.

What does HTTP/3 actually mean when you’re running NGINX?

The biggest change is simple:
HTTP/2 uses TCP.
HTTP/3 uses QUIC over UDP.

That difference changes how the operating system delivers connections to NGINX—and how NGINX workers handle them.

What This Means for NGINX

With traditional HTTPS and HTTP/2, NGINX listens for TCP connections:

listen 443 ssl;
listen [::]:443 ssl;

http2 on;

The operating system receives TCP connections on port 443, and NGINX workers process those connections.

HTTP/3 introduces a second transport:

listen 443 quic reuseport;
listen [::]:443 quic reuseport;

Now NGINX needs to receive UDP traffic on port 443.

A production configuration can therefore support both protocols simultaneously:

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

    listen 443 quic reuseport;
    listen [::]:443 quic reuseport;

    http2 on;

    server_name example.com;

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

    add_header Alt-Svc 'h3=":443"; ma=86400' always;

    ...
}

The result is not that HTTP/2 disappears.

Instead, the server can accept both TCP and UDP traffic on port 443:

                 HTTPS :443
                     │
             ┌───────┴────────┐
             │                │
          TCP/443           UDP/443
             │                │
             ▼                ▼
          HTTP/2            QUIC
                              │
                              ▼
                           HTTP/3

The browser can use HTTP/2 over TCP or HTTP/3 over QUIC, depending on what it supports and what the server advertises.

TCP and UDP Are Different Paths

NGINX is now dealing with two fundamentally different transport paths.

With HTTP/2, the stack looks like:

HTTP/2
   │
   ▼
  TLS
   │
   ▼
  TCP          ← reliable, ordered byte stream
   │
   ▼
  IP

With HTTP/3:

HTTP/3
   │
   ▼
  QUIC         ← reliable transport + TLS 1.3
   │
   ▼
  UDP          ← packet/datagram delivery
   │
   ▼
  IP

The important distinction is UDP versus QUIC.

UDP is intentionally simple. It sends independent datagrams without guaranteeing that they will arrive, arrive in order, or be retransmitted.

UDP does not provide:

  • Guaranteed delivery
  • Ordering
  • Retransmission
  • Loss recovery
  • Congestion control

If a UDP datagram is lost, UDP itself does nothing about it.

So Where Does Reliability Come From?
QUIC!

QUIC uses UDP to move packets across the network, but implements the transport features needed for a reliable connection itself.

In essence:

UDP
 │
 └── provides basic packet delivery
          │
          ▼
        QUIC
          │
          ├── Acknowledgements
          ├── Loss detection
          ├── Retransmission
          ├── Congestion control
          ├── Connection management
          ├── Connection IDs
          ├── Independent streams
          └── TLS 1.3 integration

So QUIC isn’t made reliable by UDP.
QUIC provides its own reliability while using UDP as its underlying packet transport.

Basically:

UDP moves the packets.
QUIC manages the connection.
HTTP/3 carries the HTTP semantics.

HTTP/3   →  What the web application speaks
   │
QUIC     →  How the connection is managed
   │
UDP      →  How QUIC packets are transported
   │
IP       →  How packets are routed

That distinction is fundamental to understanding what actually changes when NGINX moves from HTTP/2 to HTTP/3.

What is a Socket?

A socket is an operating-system endpoint that an application uses to send and receive network traffic.

Think of it as the doorway between NGINX and the network.

For example, when NGINX listens on HTTPS:

                NETWORK
                   │
                   ▼
              TCP :443
                   │
                   ▼
             ┌──────────┐
             │  SOCKET  │
             └──────────┘
                   │
                   ▼
                NGINX

The socket is the interface between NGINX and the operating system’s networking stack.

A socket is associated with an address, typically involving:

IP address
    +
Port
    +
Protocol

For example:

TCP 192.168.1.10:443
UDP 192.168.1.10:443

Both can use port 443 because TCP and UDP are different transport protocols. Therefore, they can have separate sockets on the same IP address and port number.

Why reuseport Matters

With HTTP/3, NGINX receives QUIC traffic over UDP.
This raises an important question:

How does UDP traffic get distributed when NGINX has multiple worker processes?

First, a worker is simply an NGINX process that handles network connections and requests.

A typical NGINX server might have:

                 NGINX
                    │
          ┌─────────┼─────────┐
          │         │         │
          ▼         ▼         ▼
       Worker 1  Worker 2  Worker 3

For TCP, the operating system has mechanisms for distributing incoming connections among NGINX workers.

UDP is different.
There are no TCP-style connections for the OS to accept and distribute.

This is where reuseport becomes important.

listen 443 quic reuseport;

With reuseport, NGINX can create a separate UDP listening socket for each worker.

The operating system can then distribute incoming UDP packets across those sockets:

                    UDP :443
                       │
              ┌────────┼────────┐
              │        │        │
              ▼        ▼        ▼
           Socket 1  Socket 2  Socket 3
              │        │        │
              ▼        ▼        ▼
           Worker 1  Worker 2  Worker 3
              │        │        │
              └────────┼────────┘
                       │
                      QUIC
                       │
                    HTTP/3

What Happens Without reuseport ?

Without:

reuseport

you can still run HTTP/3.

reuseport is not required to make QUIC or HTTP/3 function.

The difference is how UDP traffic is distributed among NGINX workers.

Without reuseport, NGINX does not use the same one-UDP-socket-per-worker arrangement. With reuseport, each worker can have its own UDP listening socket, allowing the operating system to distribute incoming QUIC packets across them.

Why Does This Matter?

The purpose of reuseport is scaling UDP/QUIC processing across NGINX workers.

On a lightly loaded server, the difference may be difficult to notice.

On a busy production server handling substantial HTTP/3 traffic, distributing UDP packet processing across multiple workers can make better use of available CPU cores and prevent a single worker/socket from becoming a bottleneck.

The simplest way to remember it:

reuseport lets multiple NGINX workers listen for UDP traffic independently, giving the operating system a way to distribute incoming QUIC traffic across them.

So, without reuseport:


                 UDP :443
                     │
                  Socket
                     │
              ┌──────┴──────┐
              │             │
          NGINX workers share
          the listening path

And with:

                 UDP :443
                     │
          ┌──────────┼──────────┐
          ▼          ▼          ▼
       Socket 1   Socket 2   Socket 3
          │          │          │
          ▼          ▼          ▼
       Worker 1   Worker 2   Worker 3

HTTP/3 works without reuseport; reuseport helps NGINX scale the UDP/QUIC workload across its workers.

HTTP/2 and HTTP/3 Can Coexist

One of the most useful aspects of an NGINX HTTP/3 deployment is that you don’t have to choose between HTTP/2 and HTTP/3.

You can have:

                HTTPS :443
                     │
          ┌──────────┴──────────┐
          │                     │
       TCP/443               UDP/443
          │                     │
       HTTP/2                 QUIC
                                │
                             HTTP/3

This allows clients that support HTTP/3 to use QUIC while other clients can continue using HTTP/2.

That makes HTTP/3 an evolutionary deployment rather than a complete replacement of your existing HTTPS stack.

How Does the Browser Know HTTP/3 is Available?

NGINX can advertise HTTP/3 using the Alt-Svc (Alternative Service) response header.

This is where Alt-Svc comes in:

add_header Alt-Svc 'h3=":443"; ma=86400' always;

Alt-Svc tells a client:
This same resource is also available through another protocol or network endpoint.

In this case, NGINX is telling the browser that the site is also available over HTTP/3.

h3=":443"
means:

  • h3 → HTTP/3
  • :443 → use port 443

ma=86400

The ma parameter means maximum age.

The value is measured in seconds:

86,400 seconds
÷ 60
= 1,440 minutes

1,440 minutes
÷ 60
= 24 hours

meaning the browser can cache this alternative-service information for 24 hours.

always
The final parameter:
tells NGINX to include the Alt-Svc header regardless of the response status code, including responses such as redirects or errors where add_header would otherwise not apply.

This ensures clients can still learn that HTTP/3 is available, even when the response isn’t a normal 2xx success.

Putting it Together

add_header Alt-Svc 'h3=":443"; ma=86400' always;

means:
Advertise HTTP/3 (h3) on port 443, allow the client to cache that information for 86,400 seconds (24 hours), and add the header regardless of the response status where NGINX’s always behavior applies.

The resulting flow looks like this:

                  Browser
                     │
                     │ HTTPS / HTTP/2
                     ▼
                  NGINX
                     │
                     │ Alt-Svc: h3=":443"
                     ▼
              Browser learns:
           HTTP/3 is available
                     │
                     │
              Future connection
                     │
                     │ QUIC / UDP :443
                     ▼
                  NGINX
                     │
                     ▼
                  HTTP/3

The important detail is that Alt-Svc does not itself switch the connection to HTTP/3.

It advertises an alternative service.

The compatible client/browser decides whether and when to use that advertised HTTP/3 endpoint, then establishes a separate QUIC connection over UDP.

For instance, Chrome supports HTTP/3 and can use it when the server advertises HTTP/3 and UDP/443 is reachable. If QUIC cannot be established, Chrome will fall back to another available HTTP version, such as HTTP/2.

HTTP/3 Still Uses TLS

Another important point is that HTTP/3 does not remove TLS.

TLS (Transport Layer Security) is the protocol that provides encryption and authentication for network communications. It protects data from being read or modified while traveling between the client and server and uses digital certificates to authenticate the server.

QUIC integrates TLS 1.3 directly into the QUIC connection establishment process.

So the stack looks roughly like this:

HTTP/3
   │
   ▼
QUIC
   │
   ├── TLS 1.3
   │
   ▼
UDP
   │
   ▼
IP

This is one of the fundamental differences from HTTP/2, where TLS sits separately above TCP.

While HTTP/2 traditionally looks like:

HTTP/2
   │
TLS
   │
TCP
   │
IP

QUIC is therefore encrypted by design. HTTP/3 runs over QUIC, which means HTTP/3 connections use TLS 1.3 for their security.

There is no unencrypted equivalent of plain HTTP over QUIC.
This is one of the reasons HTTP/3 requires HTTPS.

The NGINX Configuration Is Only One Piece

Enabling HTTP/3 in NGINX does not automatically guarantee that every client will use it.

The entire path must support UDP traffic.

That means checking:

Client
   │
   ▼
Internet
   │
   ▼
Router / Firewall
   │
   ├── TCP :443 ──► NGINX
   │
   └── UDP :443 ──► NGINX

If TCP/443 is allowed but UDP/443 is blocked, HTTP/2 can work perfectly while HTTP/3 fails.

This is one of the easiest things to overlook when troubleshooting QUIC.

The Bigger Picture

From NGINX’s perspective, HTTP/3 is not simply:

http3 on;

It introduces another transport path into the server architecture.

You now have:

                  NGINX
                    │
          ┌─────────┴─────────┐
          │                   │
       TCP :443            UDP :443
          │                   │
       HTTP/2                QUIC
                              │
                           HTTP/3

And that means administrators need to think about more than just the HTTP protocol.

They need to consider:

  • UDP/443 firewall rules
  • QUIC support
  • TLS 1.3
  • NGINX worker processes
  • reuseport
  • Alt-Svc
  • client support
  • router/NAT behavior
  • monitoring and troubleshooting

HTTP/3 therefore changes the network path into NGINX, not just the syntax of the HTTP protocol.

HTTP/3 gives NGINX a new transport path:
The important part is understanding what happens underneath:

HTTP/2 → TCP → NGINX

HTTP/3 → QUIC → UDP → NGINX

NGINX can run both side by side, while directives like quic, reuseport, and Alt-Svc control how HTTP/3 traffic reaches and scales across the server.

Thanks for reading—and as always, keep experimenting, keep learning, and keep digging beneath the surface.

Keep Us Caffeinated  ⦿ ⦿
Spotify Logo
SNUBmonkey Join our readers 36K+