HTTP/3 is the third major version of HTTP, defined in RFC 9114. It keeps HTTP semantics unchanged and replaces the transport with QUIC.
HTTP/3 keeps everything you already know about HTTP: the same methods, the same status codes, the same header fields, the same caching and authentication rules. What changes is what carries them. HTTP/1.1 and HTTP/2 run over TCP with TLS layered on top. HTTP/3 runs over QUIC, a UDP-based transport with TLS 1.3 built in.
Because HTTP semantics are unchanged, an application ported from HTTP/1.1 or HTTP/2 to HTTP/3 behaves the same way. You issue a GET, you get a status code and a body back.
| RFC | Title | What it defines |
|---|---|---|
| RFC 9114 | HTTP/3 | The mapping of HTTP onto QUIC: frames, request and response streams, unidirectional stream types, settings, server push and the HTTP/3 error codes. |
| RFC 9204 | QPACK: Field Compression for HTTP/3 | Header compression designed for QUIC, using a static table plus a dynamic table carried on dedicated encoder and decoder streams. |
| RFC 9000 | QUIC transport | The transport HTTP/3 is carried on. See QUIC. |
HTTP/2 compresses headers with HPACK, which assumes headers arrive in the order they were sent, an assumption TCP guarantees. QUIC streams can be delivered out of order, so HPACK would reintroduce head-of-line blocking at the compression layer. QPACK solves this by carrying dynamic-table updates on separate unidirectional streams and by letting a request block until only the table entries it actually depends on have arrived.
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Specification | RFC 9112 | RFC 9113 | RFC 9114 |
| Transport | TCP | TCP | QUIC over UDP |
| Encryption | Optional, TLS layered on top | Optional in the spec, TLS in practice | Always on, TLS 1.3 is part of the transport |
| Multiplexing | None. One request in flight per connection, so clients open several connections | Many streams over one TCP connection | Many streams, provided by the transport itself |
| Head-of-line blocking | Yes, at the request level | Removed at the application layer, but a lost TCP segment still stalls every stream | Removed. A lost packet only stalls the streams whose data it carried |
| Header compression | None, headers are plain text | HPACK (RFC 7541) | QPACK (RFC 9204) |
| Message format | Text | Binary frames | Binary frames over QUIC streams |
| Handshake cost | TCP handshake, then a separate TLS handshake | TCP handshake, then a separate TLS handshake | Transport and TLS combined in one flight, with 0-RTT on a resumed session |
| Connection migration | No, the connection is bound to the four-tuple | No | Yes, the connection is identified by a connection ID |
| Server push | No | Yes, deprecated in practice | Yes, driven by push IDs and a client-controlled limit |
The client supports the ten methods declared in the Th3Methods enumeration (sgcHTTP3_Types.pas). Each has a blocking method, and most also have an asynchronous variant. This table is an orientation map, see TsgcHTTP3Client for the full signatures, overloads and default parameter values.
| Method | Enumeration value | Client method | Async variant |
|---|---|---|---|
GET | h3meGet | Get | GetAsync |
HEAD | h3meHead | Head | — |
POST | h3mePost | Post | PostAsync |
PUT | h3mePut | Put | PutAsync |
DELETE | h3meDelete | Delete | DeleteAsync |
PATCH | h3mePatch | Patch | PatchAsync |
OPTIONS | h3meOptions | RequestOptions | OptionsAsync |
TRACE | h3meTrace | Trace | TraceAsync |
CONNECT | h3meConnect | Connect | ConnectAsync |
QUERY | h3meQuery | Query | QueryAsync |
Notes on two of them:
QUERY is the IETF QUERY method, a work-in-progress specification. It is a safe, idempotent method that carries a request body, so it covers the common case of a read-only query too large or too structured to fit in a URL, which today is usually forced onto POST. It is not yet a published RFC.HEAD returns the response object rather than a string, because a HEAD response carries headers and no body. It has no asynchronous variant.A client cannot assume a server speaks HTTP/3, and there is no HTTP/3 upgrade handshake equivalent to the HTTP/2 one. The usual discovery route is the Alt-Svc response header (RFC 7838): a server reached over HTTP/1.1 or HTTP/2 advertises that the same origin is also available over HTTP/3 at a given host and port, and the client remembers that for future requests. The client parses this header and surfaces it through the OnAltSvc event, so your application can decide when to switch.
A server can push a response the client did not ask for, so a client fetching a page does not need a second round trip for a resource the server knows it will need. Each push carries a push ID. The client keeps control: it advertises how many pushes it will accept with SetMaxPushId, is notified of an incoming push through OnPushPromise, and can refuse one with CancelPush.
Extended CONNECT (RFC 9220 for HTTP/3, which carries over the mechanism RFC 8441 defined for HTTP/2) turns a request stream into a tunnel for another protocol. It adds a :protocol pseudo-header to the CONNECT method, so the endpoint knows which protocol it is being asked to tunnel.
It is only available when the server advertises SETTINGS_ENABLE_CONNECT_PROTOCOL in its settings. The client exposes ExtendedConnect, which returns False when the server never advertised it, or when the response status is not a 2xx. Once the tunnel is open you write to it with TunnelSend and receive through OnTunnelData.
WebTransport is built on Extended CONNECT. It negotiates a session over an Extended CONNECT stream and then gives the application QUIC streams directly, which is what you want for low-latency, bidirectional, non-request/response traffic. The specification also offers datagrams, but they cannot be carried on the QUIC engine this library ships with, so WebTransport here is streams only. It needs both SETTINGS_ENABLE_CONNECT_PROTOCOL and a non-zero SETTINGS_WEBTRANSPORT_MAX_SESSIONS from the server, which the client checks through ServerSupportsWebTransport. Because a session needs several streams on one QUIC connection, WebTransport requires the builtin OpenSSL QUIC engine, see QUIC. Details are on the WebTransport page.
| Component | Description |
|---|---|
| TsgcHTTP3Client | Sends HTTP requests over HTTP/3, with blocking and asynchronous methods, Alt-Svc detection, server push, Extended CONNECT tunnels and WebTransport. |
| TsgcHTTP3Server | Accepts HTTP/3 connections and serves requests over QUIC. It descends from TsgcQUICServer and adds HTTP/3 framing, settings and request dispatch. |
| WebTransport | Sessions and streams over HTTP/3, negotiated with Extended CONNECT. Datagrams are not carried on the shipping QUIC engine. |
Connect to a host and port, then issue requests. The default port is 443.
oClient := TsgcHTTP3Client.Create(nil);
oClient.OnResponse := OnResponseHandler;
oClient.OnAltSvc := OnAltSvcHandler;
oClient.Connect('www.example.com', 443);
vBody := oClient.Get('https://www.example.com/');
oClient.Disconnect;
The server binds a UDP port, loads a certificate and key, and raises an event for every request.
oServer := TsgcHTTP3Server.Create(nil);
oServer.Port := 443;
oServer.QUICOptions.SSLOptions.CertFile := 'cert.pem';
oServer.QUICOptions.SSLOptions.KeyFile := 'key.pem';
oServer.OnH3Request := OnH3RequestHandler;
oServer.Start;
HTTP/3 travels over UDP, so port 443 must be reachable over UDP and not only over TCP. This is the most common reason a working HTTP/2 deployment fails to serve HTTP/3.
The server binds the address in Host. A value holding a colon is an IPv6 literal, and any other value, an IPv4 literal or a host name, is resolved over both families. An empty Host binds the dual-stack wildcard, so one listener serves IPv4 and IPv6 peers on the same port. The client is URL driven, so an IPv6 literal goes in brackets there, for example oClient.Get('https://[::1]:443/').
| Requirement | Detail |
|---|---|
| Edition | All-Access. The SGC_PACK_HTTP3 pack sits in the SGC_EDT_ALL block of sgcVer.inc. |
| Pack define | SGC_PACK_HTTP3. It ships commented out in sgcVer.inc, so it must be enabled explicitly before HTTP/3 is compiled in. |
| Feature defines | With SGC_PACK_HTTP3 and SGC_INDY_LIB both enabled, sgcVer.inc defines SGC_QUIC, SGC_HTTP3 and SGC_WEBTRANSPORT. The HTTP/3 components are compiled under SGC_HTTP3, and the WebTransport members under SGC_WEBTRANSPORT. |
| Indy | SGC_INDY_LIB is required. The HTTP/3 defines are nested inside an SGC_INDY_LIB guard. |
| OpenSSL | OpenSSL 3.5 or later, which provides the native QUIC support the components are built on by default. WebTransport additionally requires the builtin OpenSSL QUIC engine. With the TLS IOHandler set to iohNativeTLS, HTTP/3 runs without OpenSSL on the sgc QUIC engine, see SSL without OpenSSL. |
| Units | sgcHTTP.pas declares the components. HTTP/3 itself lives in the sgcHTTP3_* units, WebTransport in sgcWebTransport.pas, and the transport in the sgcQUIC_* units. |
To enable HTTP/3, edit sgcVer.inc and uncomment the pack define inside the All-Access block:
{$IFDEF SGC_EDT_ALL} { ALL-ACCESS }
{$DEFINE SGC_PACK_HTML}
{$DEFINE SGC_PACK_HTTP3}
{.$DEFINE SGC_PACK_WEBRTC}
{$ENDIF}