How to run HTTP/3 with Caddy 2

I just migrated this webserver to Caddy 2 and with it, enabled HTTP/3 support. This post will give a short explanation how you can do that.

Run Caddy 2#

The HTTP/3 feature is only available in caddy 2, make sure you run at least version 2 or higher.

This is bad:

caddy --version
Caddy v1.0.3 (h1:i9gRhBgvc5ifchwWtSe7pDpsdS9+Q0Rw9oYQmYUTw1w=)

This is good:

$ caddy version
v2.0.0-rc.2 h1:7NOaxYrsnQ5D3rcLGn2UnLZHLfBpgrfM/JNTLhjCJ1c=

(Note how the syntax changed from --version to version).

Enable HTTP/3#

In your Caddyfile, at the very top, enable the HTTP/3 feature.

{
    experimental_http3
}

ma.ttias.be {
    root * /var/www/html/ma.ttias.be/public
    file_server
    encode zstd gzip
    [...]
}

The critical part is experimental_http3.

TCP and UDP#

One major change is that HTTP/3 operates on the UDP protocol instead of TCP. Most webservers will only bind on port :80 and :443 on TCP, as they don’t need to do any UDP traffic.

By default, Caddy will also only listen on TCP:

tcp6       0      0 :::80      :::*     LISTEN      27373/caddy
tcp6       0      0 :::443     :::*     LISTEN      27373/caddy

If you enable the experimental_http3 option, it’ll listen on UDP as well.

tcp6       0      0 :::80      :::*     LISTEN      27422/caddy
tcp6       0      0 :::443     :::*     LISTEN      27422/caddy
udp6       0      0 :::443     :::*                 27422/caddy

(Note: if you’re used to running netstat | grep " LISTEN ", be aware that the UDP protocol doesn’t have a LISTEN state attached to it and won’t return any results).

If you see a UDP listener, you’re ready to receive connections.

Allow UDP traffic#

Since the shift from TCP to UDP might catch some firewall vendors by surprise, make sure you’re allowing 443/udp incoming to your server.

443/tcp is probably already allowed (or you wouldn’t be serving HTTPS sites today).

Test HTTP/3 connections#

This part is a bit more tricky, since it’s all very experimental and none of the client tooling supports HTTP/3 by default.

Via curl#

Curl has, since its recent versions, introduced a --http3 flag you can use to force HTTP/3 connections.

$ curl -I --http3 https://ma.ttias.be
HTTP/3 200
alt-svc: h3-27=":443"; ma=2592000
server: Caddy
...

You’ll need a modern version though, and most distributions don’t ship with it by default. The above was done with a custom-compiled version of curl to test HTTP/3.

$ curl --version
curl 7.70.0-DEV

Via Chrome Canary#

Chrome Canary is a nightly build of Chrome, with some more experimental features in it.

To test it out, make sure you’re running the latest version and start Chrome Canary with the --enable-quic and --quic-version=h3-27 flag. You will need to start Chrome Canary via the CLI.

(Note: the version of QUIC should match the one that is advertised in the alt-svc header.)

If you are on a Mac, you can start Chrome Canary like this:

/Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \
    --enable-quic \
    --quic-version=h3-27

The first page load will show HTTP/2. From then on, the browser has received the alt-svc header to indicate there is HTTP/3 support and will retry the next request over UDP & HTTP/3.

HTTP/3 enabled in Firefox

Because HTTP/3 is still evolving, Chrome shows the exact revision of the HTTP/3 protocol being used.

Via Firefox Nightly#

Just like Chrome, Firefox has an experimental build called Firefox Nightly .

First, enable HTTP/3. Navigate to about:config and find the option network.http.http3.enabled. Doubleclick it to toggle it from disabled to enabled.

Enable HTTP/3 in Firefox

Refresh your site and it should show HTTP/3 in the inspector. Make sure the Protocol column is shown.

HTTP/3 enabled in Firefox

Conclusion#

HTTP/3 is still very experimental. Server-side support is slowly coming, but client-side support is disabled by default everywhere.

We’re still in the trial & error phase, I’m curious to see when the the first browsers will ship with HTTP/3 enabled.

It’s a chicken and egg situation right now: there’s little incentive for servers to implement it, as no clients support it. And why should clients push forward, if no servers provide the support?