When an HTTP/3 connection is old enough that it needs explicitly resuming, it seems the client_ip matcher information becomes invalid which breaks LAN filtering (fortunately in a fails-closed state for me, but might not be for everyone).
Oct 27 08:58:07 sandbox caddy[164427]: {"level":"info","ts":1730019487.2480054,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"10.0.10.38","remote_port":"63281","client_ip":"10.0.10.38","proto":"HTTP/2.0","method":"GET","host":"sandbox.lan","uri":"/","headers":{"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8"],"Te":["trailers"],"Accept-Language":["en-GB,en;q=0.5"],"Sec-Fetch-Site":["none"],"Dnt":["1"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Mode":["navigate"],"Priority":["u=0, i"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Gpc":["1"],"Sec-Fetch-User":["?1"],"Cookie":["REDACTED"],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"sandbox.lan"}},"bytes_read":0,"user_id":"","duration":0.000031453,"size":6,"status":200,"resp_headers":{"Alt-Svc":["h3=\":443\"; ma=2592000"],"Content-Type":["text/plain; charset=utf-8"],"Server":["Caddy"]}}
Oct 27 08:59:13 sandbox caddy[164427]: {"level":"info","ts":1730019553.11203,"logger":"http.log.access","msg":"handled request","request":{"remote_ip":"10.0.10.38","remote_port":"56262","client_ip":"10.0.10.38","proto":"HTTP/3.0","method":"GET","host":"sandbox.lan","uri":"/","headers":{"Alt-Used":["sandbox.lan"],"Priority":["u=0, i"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0"],"Sec-Gpc":["1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Mode":["navigate"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8"],"Accept-Language":["en-GB,en;q=0.5"],"Sec-Fetch-Site":["cross-site"],"Dnt":["1"],"Cookie":["REDACTED"],"Upgrade-Insecure-Requests":["1"]},"tls":{"resumed":true,"version":772,"cipher_suite":4865,"proto":"h3","server_name":"sandbox.lan"}},"bytes_read":0,"user_id":"","duration":0.000038118,"size":16,"status":403,"resp_headers":{"Server":["Caddy"],"Content-Type":["text/plain; charset=utf-8"],"Date":["Sun, 27 Oct 2024 08:59:13 GMT"]}}
The client_ip in the log still appears correct, but is not being used by the matcher so the second (resumed) try returns a 403.
Testing is unfortunately a little slow. Only Firefox even seems to attempt HTTP/3 for some reason (Chrome(ish) and Safari don't try, curl is one-shot as far as I know). And it will reuse the connection without an explicit "resumed" handshake (whatever that entails) unless it's left idle for 1 minute between tries.
In the logs above, the first request was HTTP/2 and the followup switched to HTTP/3 but I also have examples where a successful HTTP/3 request is made to start with, which is why I think the "resumed" is important.
When an HTTP/3 connection is old enough that it needs explicitly resuming, it seems the
client_ip
matcher information becomes invalid which breaks LAN filtering (fortunately in a fails-closed state for me, but might not be for everyone).Example Caddyfile:
Gives the logs:
The
client_ip
in the log still appears correct, but is not being used by the matcher so the second (resumed) try returns a 403.Testing is unfortunately a little slow. Only Firefox even seems to attempt HTTP/3 for some reason (Chrome(ish) and Safari don't try, curl is one-shot as far as I know). And it will reuse the connection without an explicit "resumed" handshake (whatever that entails) unless it's left idle for 1 minute between tries.
In the logs above, the first request was HTTP/2 and the followup switched to HTTP/3 but I also have examples where a successful HTTP/3 request is made to start with, which is why I think the "resumed" is important.