Skip to main content
Version: 3.19.0

Configure HTTP/3 QUIC Between Client and APISIX

HTTP/3 is the third major version of the Hypertext Transfer Protocol (HTTP). Unlike its predecessors which rely on TCP, HTTP/3 is based on QUIC (Quick UDP Internet Connections) protocol. It brings several benefits that collectively result in reduced latency and improved performance:

  • enabling seamless transition between different network connections, such as switching from Wi-Fi to mobile data.
  • eliminating head-of-line blocking, so that a lost packet does not block all streams.
  • negotiating TLS versions at the same time as the TLS handshakes, allowing for faster connections.
  • providing encryption by default, ensuring that all data transmitted over an HTTP/3 connection is protected and confidential.
  • providing zero round-trip time (0-RTT) when communicating with servers that clients have already established connections to.

APISIX currently supports HTTP/3 connections between downstream clients and APISIX. HTTP/3 connections with upstream services are not yet supported.

caution

This feature is currently experimental and not recommended for production use.

This guide will show you how to configure APISIX to enable HTTP/3 connections between client and APISIX.

Prerequisite(s)​

  • Install Docker.
  • Install static-curl or other curl that has HTTP/3 support.
  • Follow the Getting Started tutorial to start a new APISIX instance in Docker or on Kubernetes.
  • Install jq to encode the PEM files as JSON strings.
  • If you use the Admin API examples and authentication is enabled, export an admin-role key as ADMIN_API_KEY. Configure the key under deployment.admin.admin_key. The Docker quickstart disables authentication for local evaluation; keep it enabled in production.

Enable HTTP/3 in APISIX​

Copy the complete active config.yaml to the host and keep a backup:

docker cp apisix-quickstart:/usr/local/apisix/conf/config.yaml ./config.yaml
cp ./config.yaml ./config.yaml.backup

In the local config.yaml, update the listener for port 9443 under apisix.ssl.listen; if that listener does not exist, add it to the existing list. The following is a fragment, not a replacement file:

apisix:
ssl:
listen:
- port: 9443
enable_http3: true

Keep the existing Admin API authentication, allowlist, etcd, and plugin settings. Do not add a second apisix or ssl mapping. Copy the candidate file into the container, then validate it and reload APISIX only if validation succeeds:

docker cp ./config.yaml apisix-quickstart:/usr/local/apisix/conf/config.yaml
docker exec apisix-quickstart apisix test && \
docker exec apisix-quickstart apisix reload

If validation fails, restore the backup before making another change:

docker cp ./config.yaml.backup apisix-quickstart:/usr/local/apisix/conf/config.yaml

Keep the backup until HTTP/3 verification succeeds.

Generate Certificates and Keys​

HTTP/3 requires TLS. You can purchase the certificates or self-generate them, whichever is applicable.

To self-generate, first generate the certificate authority (CA) key and certificate:

openssl genrsa -out ca.key 2048 && \
openssl req -new -sha256 -key ca.key -out ca.csr -subj "/CN=ROOTCA" && \
openssl x509 -req -days 36500 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.crt

Next, generate the key and certificate with the common name test.com for APISIX, and sign with the CA certificate:

openssl genrsa -out server.key 2048 && \
openssl req -new -sha256 -key server.key -out server.csr -subj "/CN=test.com" && \
openssl x509 -req -days 36500 -sha256 -extensions v3_req \
-CA ca.crt -CAkey ca.key -CAserial ca.srl -CAcreateserial \
-in server.csr -out server.crt

Configure HTTPS​

Create the SSL object with an SNI that matches the certificate's test.com common name. jq reads the certificate and private key files and encodes their newlines as valid JSON:

jq -n --rawfile cert server.crt --rawfile key server.key \
'{id: "quickstart-tls-client-ssl", sni: "test.com", cert: $cert, key: $key}' \
| curl -i "http://127.0.0.1:9180/apisix/admin/ssls" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @-

Create a Route in APISIX​

Create a sample route to httpbin.org:

curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id":"httpbin-route",
"uri":"/get",
"upstream": {
"type":"roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'

Verify HTTP/3 Connections between Client and APISIX​

Send a request to the route:

curl -kv --http3-only \
-H "Host: test.com" \
--resolve "test.com:9443:127.0.0.1" "https://test.com:9443/get"

You should receive an HTTP/3 200 response similar to the following:

* Added test.com:9443:127.0.0.1 to DNS cache
* Hostname test.com was found in DNS cache
* Trying 127.0.0.1:9443...
* QUIC cipher selection: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256
* Skipped certificate verification
* Connected to test.com (127.0.0.1) port 9443
* using HTTP/3
* [HTTP/3] [0] OPENED stream for https://test.com:9443/get
* [HTTP/3] [0] [:method: GET]
* [HTTP/3] [0] [:scheme: https]
* [HTTP/3] [0] [:authority: test.com]
* [HTTP/3] [0] [:path: /get]
* [HTTP/3] [0] [user-agent: curl/8.7.1]
* [HTTP/3] [0] [accept: */*]
> GET /get HTTP/3
> Host: test.com
> User-Agent: curl/8.7.1
> Accept: */*
>
* Request completely sent off
< HTTP/3 200
...
{
"args": {},
"headers": {
"Accept": "*/*",
"Content-Length": "0",
"Host": "test.com",
"User-Agent": "curl/8.7.1",
"X-Amzn-Trace-Id": "Root=1-6656013a-27da6b6a34d98e3e79baaf5b",
"X-Forwarded-Host": "test.com"
},
"origin": "172.19.0.1, 123.40.79.456",
"url": "http://test.com/get"
}
* Connection #0 to host test.com left intact

Next Steps​

You have learned how to configure HTTP/3 between client and APISIX. HTTP/3 connections with upstream services are not yet supported and any contribution is welcome.