How to run HTTP/3 with Caddy 2

Want to help support this blog? Try out Oh Dear, the best all-in-one monitoring tool for your entire website, co-founded by me (the guy that wrote this blogpost). Start with a 10-day trial, no strings attached.

We offer uptime monitoring, SSL checks, broken links checking, performance & cronjob monitoring, branded status pages & so much more. Try us out today!

Profile image of Mattias Geniar

Mattias Geniar, April 10, 2020

Follow me on Twitter as @mattiasgeniar

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.

} {
    root * /var/www/html/
    encode zstd gzip

The critical part is experimental_http3.


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
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\\ Chrome\ Canary \
    --enable-quic \

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


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?

Want to subscribe to the cron.weekly newsletter?

I write a weekly-ish newsletter on Linux, open source & webdevelopment called cron.weekly.

It features the latest news, guides & tutorials and new open source projects. You can sign up via email below.

No spam. Just some good, practical Linux & open source content.