1. The problem I’m having:
I want to deploy Caddy on an IP6 network which only supports MTU 1310. When I switch to MTU 1310, Caddy doesn’t reply to the handshake request. I understand QUIC should support down to 1280.
When I switch back to 1500, everything works.
2. Error messages and/or full log output:
An example timeout:
$ sudo ip netns exec http3ns2 bash -c " all_proxy="" https_proxy="" curl --http3-only -v --cert /etc/caddy/certs/client.crt --key /etc/caddy/certs/client.key --resolve api.airlink.local:443:[fd00:dead::1] --cacert /etc/caddy/certs/rootCA.pem https://api.airlink.local:443/v0/client/query?obcu_id=1"
* Added api.airlink.local:443:[fd00:dead::1] to DNS cache
* Uses proxy env variable no_proxy == '127.0.0.1,localhost,internal.domain'
* Hostname api.airlink.local was found in DNS cache
* Trying [fd00:dead::1]:443...
* QUIC cipher selection: TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256
* CAfile: /etc/caddy/certs/rootCA.pem
* CApath: none
* ngtcp2_conn_handle_expiry returned error: ERR_HANDSHAKE_TIMEOUT
* Failed to connect to api.airlink.local port 443 after 10002 ms: Failed sending data to the peer
* Closing connection
curl: (55) ngtcp2_conn_handle_expiry returned error: ERR_HANDSHAKE_TIMEOUT
Caddy doesn’t actually error out, it just doesn’t forward to the reverse proxy etc.
2025/04/03 20:04:03.394 DEBUG tls.handshake choosing certificate {"identifier": "api.airlink.local", "num_choices": 1}
2025/04/03 20:04:03.394 DEBUG tls.handshake custom certificate selection results {"identifier": "api.airlink.local", "subjects": ["49677b3bcbb760a39ea5cf20db06c490.non-safety.unit.airlink.local", "airlink.local", "api.airlink.local", "console.airlink.local", "*.mgmt.airlink.local", "non-safety.role.airlink.local"], "managed": false, "issuer_key": "", "hash": "0778f5c453138465c7629af68378fec46884d5423c7b840a2f6f4d3d327f8612"}
2025/04/03 20:04:03.394 DEBUG tls.handshake matched certificate in cache {"remote_ip": "fd00:dead::2", "remote_port": "37156", "subjects": ["49677b3bcbb760a39ea5cf20db06c490.non-safety.unit.airlink.local", "airlink.local", "api.airlink.local", "console.airlink.local", "*.mgmt.airlink.local", "non-safety.role.airlink.local"], "managed": false, "expiration": "2152/02/07 12:00:01.000", "hash": "0778f5c453138465c7629af68378fec46884d5423c7b840a2f6f4d3d327f8612"}
Caddy does successfully reply with an ACK_ECN packet instead of completing the handshake in the normal way.
3. Caddy version:
2.9.1
4. How I installed and ran Caddy:
I installed via NixOS and also tried the binary from the Caddy Github repo
a. System environment:
NixOS & Yocto
b. Command:
sudo ip netns exec http3ns1 bash -c "CADDY_LOG_LEVEL=DEBUG /nix/store/vdjnfdjnnqspc67fjys692b4hishmljd-caddy-2.9.1/bin/caddy run --config /home/john/caddy_config --adapter caddyfile"
c. Service/unit/compose file:
NA
d. My complete Caddy config:
{
auto_https disable_redirects
debug
servers {
protocols h3
}
}
api.airlink.local:443 {
# TODO: update to new IP when ready
bind udp6/[fd00:3d2::300:64]
tls /run/http/ssl/server.crt /run/http/ssl/server.key {
client_auth {
mode require_and_verify
trusted_ca_cert_file /run/http/ssl/roots.crt
}
}
# Match only paths that begin with "client/"
@client_routes {
path_regexp client ^/v0/(client/.+)$
}
handle @client_routes {
#TODO: make port configurable
reverse_proxy localhost:8000
}
respond "Access denied" 403
}
5. Links to relevant resources:
NA