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.