Running HTTP/3 with Caddy in 2026: it just works

When I wrote about running HTTP/3 with Caddy in 2020 , it was a ritual. You opted in with an experimental_http3 global option, the protocol was still draft h3-27, and to test it you needed a custom-compiled curl and a browser nightly with the right flags flipped. It worked, but it was clearly the bleeding edge.

In 2026 there’s nothing to do. That’s the whole post, but it’s worth showing how little “nothing” is.

The config#

Here’s a complete Caddyfile that serves HTTP/3:

ma.ttias.be {
    root * /var/www/html/public
    file_server
}

That’s the lot. There’s no HTTP/3 directive, because Caddy has enabled it by default since v2.6 (September 2022). The experimental_http3 option I used in 2020 is gone; leaving it in your config now stops Caddy from starting.

I tested this on the current release, v2.11.4. A plain config with nothing HTTP/3-specific anywhere answers an --http3 request:

$ curl --http3 -k -i https://caddyhost/
HTTP/3 200
server: Caddy
content-type: text/plain; charset=utf-8
content-length: 20

served over HTTP/3.0

That served over HTTP/3.0 is Caddy echoing back the protocol it negotiated. No flags, no opt-in. The h3 doesn’t even carry a draft suffix anymore, the way it read h3-27 in 2020.

The one thing you still do#

HTTP/3 runs over UDP, and most firewalls only allow TCP on 443. So the single action left is opening 443/udp inbound. Caddy binds the UDP socket on its own (I confirmed it does on a default config), but if your firewall drops UDP, clients silently fall back to HTTP/2 over TCP and you’ll never see h3. I went into the why of this in the nginx version of this post , and it’s identical here: check the firewall before you debug anything else.

What still matters in 2026#

Most “enable HTTP/3” guides stop at “it’s on”. A few things matter in 2026 that didn’t back then.

There’s probably a load balancer or CDN in front of you. If Caddy runs behind Cloudflare, a cloud load balancer, or any edge that terminates TLS, HTTP/3 is negotiated between the client and the edge, not between the edge and your origin. Your origin Caddy might only ever speak HTTP/2 or even HTTP/1.1 to the balancer, and that’s correct. Don’t go chasing end-to-end h3 to your origin when the edge is already giving your users HTTP/3.

0-RTT is there, and it’s a trade-off. Caddy supports QUIC’s early data, which lets a returning client send its first request in the opening packet. Faster, but 0-RTT data is replayable by design, so it’s only safe for idempotent requests. Caddy ships the safe defaults; just know the feature exists when you read about it.

You can pin or disable the protocols. Once in a while a broken middlebox mangles QUIC and you want to force clients back to TCP. The modern knob is the servers global option:

{
    servers {
        protocols h1 h2 h3
    }
}

List only h1 h2 and Caddy stops advertising HTTP/3. I validated that config on 2.11.4; it’s the supported replacement for the old experimental flag, except now it tunes the protocols instead of opting you in.

Where it landed#

The arc from 2020 to now is the same one I traced for QUIC and HTTP/3 as a protocol : an experiment that needed custom builds became a finished standard you get for free. With Caddy it’s starker than most, because the server was already automating your HTTPS certificates, and now it automates HTTP/3 the same way. Run a current Caddy, open 443/udp, and you’re serving it. The 2020 ritual is still online as a record of how it used to be; you just don’t need any of it now.