HTTP/3 & QUIC

HTTP/3 replaces TCP with QUIC (UDP-based transport) to eliminate head-of-line blocking, reduce connection latency, and enable 0-RTT connection resumption. bext supports HTTP/3 as a Pro feature, running a QUIC listener alongside the existing HTTP/1.1 and HTTP/2 listeners.

Enabling HTTP/3

HTTP/3 is opt-in. Add the [http3] section to your bext.config.toml:

[http3]
enabled = true
max_concurrent_streams = 100
idle_timeout_ms = 30000

HTTP/3 requires TLS 1.3 (QUIC mandates it), so you must also have TLS enabled:

[tls]
mode = "auto"
acme_email = "ops@example.com"

[http3]
enabled = true

When both conditions are met, bext binds a UDP socket on the same port as the TCP listener (typically 443) and begins accepting QUIC connections.

How It Works

With HTTP/3 enabled, bext runs three listeners simultaneously:

Protocol Transport Port
HTTP/1.1 + HTTP/2 TCP + TLS 443
HTTP/3 QUIC (UDP) + TLS 1.3 443
HTTP redirect TCP (plain) 80

All three share the same TLS certificate. The QUIC listener uses the same cert/key as the TCP TLS acceptor, whether provisioned via ACME, manually provided, or self-signed.

Alt-Svc Header Advertising

Browsers discover HTTP/3 support through the Alt-Svc response header. When HTTP/3 is enabled, bext automatically adds this header to every HTTP/1.1 and HTTP/2 response:

Alt-Svc: h3=":443"; ma=86400

This tells the browser: "This server supports HTTP/3 on the same host, port 443, and you can cache this information for 86400 seconds (24 hours)." On subsequent requests, the browser will attempt a QUIC connection.

The ma (max-age) value controls how long the browser caches the Alt-Svc advertisement. A longer value means fewer fallback-to-TCP connections, but also means clients take longer to stop trying QUIC if you disable it.

0-RTT Connection Resumption

QUIC supports 0-RTT (zero round-trip time) connection resumption. When a client reconnects to a server it has visited before, it can send application data in the very first packet -- before the TLS handshake completes. This reduces perceived latency by one full round-trip.

bext accepts 0-RTT data by default when HTTP/3 is enabled. Keep in mind that 0-RTT data is inherently replayable (an attacker could capture and resend the initial packet), so bext only allows idempotent requests (GET, HEAD) in 0-RTT. Non-idempotent methods (POST, PUT, DELETE) are deferred until the handshake completes.

Transport Parameters

The [http3] configuration exposes key QUIC transport parameters:

[http3]
enabled = true
max_concurrent_streams = 100   # Max simultaneous request streams per connection
idle_timeout_ms = 30000        # Close idle connections after 30 seconds

max_concurrent_streams

Controls how many HTTP request/response exchanges can happen simultaneously on a single QUIC connection. The default (100) is suitable for most web applications. Increase it for pages that trigger many parallel subresource fetches, or decrease it for API servers where connections are short-lived.

idle_timeout_ms

The inactivity timeout for QUIC connections. After this period with no data transfer, the connection is gracefully closed. The default is 30 seconds. For long-polling or SSE endpoints, consider increasing this:

[http3]
enabled = true
idle_timeout_ms = 120000   # 2 minutes for long-lived connections

HTTP/2 Configuration

HTTP/2 is enabled automatically when TLS is active (no configuration needed). You can tune its parameters in the [http2] section:

[http2]
enabled = true                  # Default: true when TLS is active
max_concurrent_streams = 100    # Default: 100
initial_window_size = 65535     # Default: 64KB (HTTP/2 spec default)
h2c = false                     # HTTP/2 cleartext (no TLS), for internal networks

The h2c option enables HTTP/2 over plain TCP, which is useful for communication between services on a trusted internal network where TLS overhead is unnecessary.

Browser Compatibility

HTTP/3 support in major browsers (as of 2025):

Browser HTTP/3 Support
Chrome 87+ Yes (default since Chrome 94)
Firefox 88+ Yes (default since Firefox 91)
Safari 14+ Yes (macOS Big Sur and later)
Edge 87+ Yes (Chromium-based)
curl 7.66+ Yes (with --http3 flag)

All modern browsers support HTTP/3 and will upgrade automatically when they see the Alt-Svc header. Older browsers and clients that do not support QUIC continue using HTTP/2 or HTTP/1.1 over TCP transparently.

Fallback Behavior

HTTP/3 is designed to coexist with HTTP/2 and HTTP/1.1. If a QUIC connection fails (UDP blocked by a firewall, network path issue, or server overload), the browser falls back to TCP immediately. bext handles this gracefully:

- The TCP listener is always active regardless of HTTP/3 settings

- Clients that never receive the Alt-Svc header (proxied connections, some corporate networks) use TCP from the start

- If the QUIC listener encounters an error at startup (port already in use, UDP socket unavailable), bext logs a warning and continues with TCP-only operation

This fallback mechanism means enabling HTTP/3 is safe for production: clients that can use it benefit from lower latency, while all others continue working exactly as before.

Full Example

A production configuration with all protocol layers:

[server]
listen = "0.0.0.0:443"

[tls]
mode = "auto"
acme_email = "ops@example.com"
ocsp_stapling = true

[http2]
max_concurrent_streams = 200
initial_window_size = 131072   # 128KB window for high-bandwidth clients

[http3]
enabled = true
max_concurrent_streams = 200
idle_timeout_ms = 60000

[license]
key = "BEXT-PRO-..."

This serves HTTP/1.1, HTTP/2, and HTTP/3 on the same port with automatic TLS, OCSP stapling, and generous concurrency limits.