TLS handshake error on reverse proxy behind an ISP router

1. The problem I’m having:

Hello everyone !
This is my first experience with caddy and reverse proxy things.
Here is my setup :

Internet → ISP router (called box in France) → RPI with apps running

My isp router has a dynamic IP and provide a ddns service so i registered a domain (xxx.xubi.org) linked to the ip at any time. The rpi has a static ip and is in the DMZ of the router.

I use caddy to reverse proxy my apps on LAN and everything works as expected.

When i try to reverse proxy on internet with my domain and subdomains the TLS handshake fails (cf. curl -vL https://qui.etxea.xubi.org).

Solving this issue is way beyond my knowledge. Thx in advance for the help.

PS : using the caddy’s certificates directly in the webui apps for https support makes the apps available with working https support.

2. Error messages and/or full log output:

juin 01 19:13:39 rpi systemd[1]: Starting Caddy web server...
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.565836,"msg":"using config from file","file":"/home/al
arm/Caddy/Caddyfile"}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.576413,"msg":"adapted config to JSON","adapter":"caddy
file"}
juin 01 19:13:40 rpi caddy[8822]: {"level":"warn","ts":1780334020.5765247,"msg":"Caddyfile input is not formatted; run '
caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/home/alarm/Caddy/Caddyfile","line":47}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.5911298,"logger":"tls.cache.maintenance","msg":"starte
d background certificate maintenance","cache":"0x7686fa43fa80"}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.5965667,"logger":"http.auto_https","msg":"server is li
stening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https
_port":443}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.597822,"logger":"http.auto_https","msg":"enabling auto
matic HTTP->HTTPS redirects","server_name":"srv0"}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.6013272,"logger":"tls.cache.maintenance","msg":"stoppe
d background certificate maintenance","cache":"0x7686fa43fa80"}
juin 01 19:13:40 rpi caddy[8822]: {"level":"info","ts":1780334020.6014304,"logger":"http","msg":"servers shutting down w
ith eternal grace period"}
juin 01 19:13:40 rpi caddy[8822]: Valid configuration
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.936228,"msg":"maxprocs: Leaving GOMAXPROCS=4: CPU quot
a undefined"}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.936306,"msg":"GOMEMLIMIT is updated","GOMEMLIMIT":3579
509145,"previous":9223372036854775807}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.9363184,"msg":"using config from file","file":"/home/a
larm/Caddy/Caddyfile"}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.936328,"msg":"adapted config to JSON","adapter":"caddy
file"}
juin 01 19:13:40 rpi caddy[8831]: {"level":"warn","ts":1780334020.936336,"msg":"Caddyfile input is not formatted; run 'c
addy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/home/alarm/Caddy/Caddyfile","line":47}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.99222,"logger":"admin","msg":"admin endpoint started",
"address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.993235,"logger":"tls.cache.maintenance","msg":"started
 background certificate maintenance","cache":"0x279c3553e400"}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.9947999,"logger":"http.auto_https","msg":"server is li
stening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https
_port":443}
juin 01 19:13:40 rpi caddy[8831]: {"level":"info","ts":1780334020.9949386,"logger":"http.auto_https","msg":"enabling aut
omatic HTTP->HTTPS redirects","server_name":"srv0"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.0236213,"logger":"http","msg":"enabling HTTP/3 listene
r","addr":":443"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.0243196,"logger":"http.log","msg":"server running","na
me":"srv0","protocols":["h1","h2","h3"]}
juin 01 19:13:41 rpi caddy[8831]: {"level":"warn","ts":1780334021.0372763,"logger":"http","msg":"HTTP/2 skipped because
it requires TLS","network":"tcp","addr":":80"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"warn","ts":1780334021.0373309,"logger":"http","msg":"HTTP/3 skipped because
it requires TLS","network":"tcp","addr":":80"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.037343,"logger":"http.log","msg":"server running","nam
e":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.0373616,"logger":"http","msg":"enabling automatic TLS
certificate management","domains":["qbt.etxea.xubi.org","kodi.rpi.local","etxea.xubi.org","qui.rpi.local","qbt.rpi.local
","rpi.local","kodi.etxea.xubi.org","qui.etxea.xubi.org"]}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.0797873,"logger":"tls","msg":"storage cleaning happene
d too recently; skipping for now","storage":"FileStorage:/home/alarm/Caddy/caddy","instance":"a66a6b78-5249-4d11-aa48-6d
1d81325af8","try_again":1780420421.0797825,"try_again_in":86399.99999813}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.080087,"logger":"tls","msg":"finished cleaning storage
 units"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.171356,"logger":"pki.ca.local","msg":"root certificate
 is already trusted by system","path":"storage:pki/authorities/local/root.crt"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.1749895,"msg":"autosaved config (load with --resume fl
ag)","file":"/home/alarm/Caddy/caddy/autosave.json"}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.1752706,"msg":"serving initial configuration"}
juin 01 19:13:41 rpi systemd[1]: Started Caddy web server.
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.4403834,"logger":"http.acme_client","msg":"got renewal
 info","names":["qui.etxea.xubi.org"],"window_start":1786665599,"window_end":1786838399,"selected_time":1786804691,"rech
eck_after":1780355621.440335,"explanation_url":""}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.448734,"logger":"tls","msg":"updated and stored ACME r
enewal information","identifiers":["qui.etxea.xubi.org"],"cert_hash":"71ca398d3c968b384744e1b4a7efc5f96e4d1d5406de8271ec
621d617ca1189d","ari_unique_id":"lEWf2lFHcdVzrqwdqwhyEm2tfGI.AM8x7aC-jSY7ltjkZVnGkAA","cert_expiry":1787961599,"selected
_time":1786731821,"next_update":1780355621.440335,"explanation_url":""}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.4631512,"logger":"http.acme_client","msg":"got renewal
 info","names":["kodi.etxea.xubi.org"],"window_start":1786665599,"window_end":1786838399,"selected_time":1786799447,"rec
heck_after":1780355621.4631217,"explanation_url":""}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.4753284,"logger":"tls","msg":"updated and stored ACME
renewal information","identifiers":["kodi.etxea.xubi.org"],"cert_hash":"7fe63e4038530597ca745a75cb45599be77fc42109d754ec
4495004859d2ee82","ari_unique_id":"lEWf2lFHcdVzrqwdqwhyEm2tfGI.Ljuyjlo9jBkUqJKtMJwvZA","cert_expiry":1787961599,"selecte
d_time":1786788716,"next_update":1780355621.4631217,"explanation_url":""}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.482924,"logger":"http.acme_client","msg":"got renewal
info","names":["qbt.etxea.xubi.org"],"window_start":1786665599,"window_end":1786838399,"selected_time":1786717895,"reche
ck_after":1780355621.4828992,"explanation_url":""}
juin 01 19:13:41 rpi caddy[8831]: {"level":"info","ts":1780334021.4914784,"logger":"tls","msg":"updated and stored ACME
renewal information","identifiers":["qbt.etxea.xubi.org"],"cert_hash":"8199b94aded20fd36d4e3b582465e547db8eaeb2f159c8f2a
5115200fd2382da","ari_unique_id":"lEWf2lFHcdVzrqwdqwhyEm2tfGI.AOd5LOEcMfwJz1BlpbyLdyE","cert_expiry":1787961599,"selecte
d_time":1786695134,"next_update":1780355621.4828992,"explanation_url":""}
juin 01 19:30:10 rpi caddy[8831]: {"level":"warn","ts":1780335010.7040148,"logger":"http.handlers.reverse_proxy","msg":"
aborting with incomplete response","upstream":"localhost:8096","duration":0.154198024,"request":{"remote_ip":"192.168.1.
11","remote_port":"51774","client_ip":"192.168.1.11","proto":"HTTP/2.0","method":"GET","host":"rpi.local","uri":"/jellyf
in/Audio/35d0a7369172a9c197929fad9e19fa99/universal?UserId=d0caca5f4234484b880bc072f26eb49f&DeviceId=TW96aWxsYS81LjAgKFg
xMTsgTGludXggeDg2XzY0OyBydjoxNTEuMCkgR2Vja28vMjAxMDAxMDEgRmlyZWZveC8xNTEuMHwxNzgwMzI3NTY4MDMw&MaxStreamingBitrate=140000
000&Container=opus%2Cwebm%7Copus%2Cts%7Cmp3%2Cmp3%2Caac%2Cm4a%7Caac%2Cm4b%7Caac%2Cflac%2Cwebma%2Cwebm%7Cwebma%2Cwav%2Cog
g&TranscodingContainer=mp4&TranscodingProtocol=hls&AudioCodec=aac&api_key=34ef42b3dce84af2a35a7fd427f6d5f1&PlaySessionId
=1780333707040&StartTimeTicks=0&EnableRedirection=true&EnableRemoteMedia=false&EnableAudioVbrEncoding=true","headers":{"
Accept":["audio/webm,audio/ogg,audio/wav,audio/*;q=0.9,application/ogg;q=0.7,video/*;q=0.6,*/*;q=0.5"],"Range":["bytes=0
-"],"Sec-Fetch-Mode":["cors"],"Sec-Fetch-Site":["same-origin"],"X-Forwarded-Proto":["https"],"Accept-Language":["fr,fr-F
R;q=0.9,en-US;q=0.8,en;q=0.7"],"User-Agent":["Mozilla/5.0 (X11; Linux x86_64; rv:151.0) Gecko/20100101 Firefox/151.0"],"
Sec-Gpc":["1"],"Te":["trailers"],"X-Forwarded-For":["192.168.1.11"],"X-Forwarded-Host":["rpi.local"],"Sec-Fetch-Dest":["
audio"],"Accept-Encoding":["identity"],"Priority":["u=4"],"Via":["2.0 Caddy"]},"tls":{"resumed":false,"version":772,"cip
her_suite":4867,"proto":"h2","server_name":"rpi.local","ech":false}},"error":"reading: context canceled"}

3. Caddy version:

caddy 2.11.3-1

4. How I installed and ran Caddy:

via distribution package and with the provided systemd unit service

4a. System environment:

ArchlinuxARM (aarch64)

4b. Command:

systemctl start caddy

4c. Service/unit/compose file:

[Unit]
Description=Caddy web server
Documentation= Welcome — Caddy Documentation
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=14400
StartLimitBurst=10

[Service]
Type=notify
User=alarm
Group=wheel
Environment=XDG_DATA_HOME=/home/alarm/Caddy
Environment=XDG_CONFIG_HOME=/etc
ExecStartPre=/usr/bin/caddy validate --config /home/alarm/Caddy/Caddyfile
ExecStart=/usr/bin/caddy run --config /home/alarm/Caddy/Caddyfile
ExecReload=/usr/bin/caddy reload --config /home/alarm/Caddy/Caddyfile --force
ExecStopPost=/usr/bin/rm -f /run/caddy/admin.socket
RestartPreventExitStatus=1
Restart=on-failure
RestartSec=5s
TimeoutStopSec=5s

LimitNOFILE=1048576
LimitNPROC=512

AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
DevicePolicy=closed
LockPersonality=true
MemoryAccounting=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true
PrivateDevices=true
PrivateTmp=true
ProcSubset=pid
ProtectClock=true
ProtectControlGroups=true
ProtectHome=false
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectProc=invisible
ProtectSystem=strict
RemoveIPC=true
ReadWritePaths=/var/lib/caddy /var/log/caddy /run/caddy /home/alarm/Caddy
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true

[Install]
WantedBy=multi-user.target

4d. My complete Caddy config:

rpi.local {
	tls internal
	redir /jellyfin /jellyfin/
	reverse_proxy /jellyfin/* localhost:8096
	redir /prowlarr /prowlarr/
	reverse_proxy /prowlarr/* localhost:9696
}
kodi.rpi.local {
	tls internal
	reverse_proxy localhost:8585
}
qui.rpi.local {
	tls internal
	reverse_proxy localhost:7476
}
qbt.rpi.local {
	tls internal
	reverse_proxy localhost:8080
}
etxea.xubi.org {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	redir /jellyfin /jellyfin/
	reverse_proxy /jellyfin/* localhost:8096
	redir /prowlarr /prowlarr/
	reverse_proxy /prowlarr/* localhost:9696
}
kodi.etxea.xubi.org {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:8585
}
qui.etxea.xubi.org {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:7476
}
qbt.etxea.xubi.org {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:8080
}

5. What I already tried, and links to relevant resources:

My cf. curl -vL https://qui.etxea.xubi.org output.


Trying [2a01:cb18:82e1:4000:a27:a8ff:fec5:6386]:443…

Host qui.etxea.xubi.org:443 was resolved.

IPv6: 2a01:cb18:82e1:4000:a27:a8ff:fec5:6386

IPv4: 92.138.191.247

ALPN: curl offers h2,http/1.1

TLSv1.3 (OUT), TLS handshake, Client hello (1):

SSL Trust Anchors:

CAfile: /etc/ssl/certs/ca-certificates.crt

TLSv1.3 (IN), TLS handshake, Server hello (2):

TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):

TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):

TLSv1.3 (IN), TLS handshake, Request CERT (13):

TLSv1.3 (IN), TLS handshake, Certificate (11):

TLSv1.3 (IN), TLS handshake, CERT verify (15):

TLSv1.3 (IN), TLS handshake, Finished (20):

TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):

TLSv1.3 (OUT), TLS handshake, Certificate (11):

TLSv1.3 (OUT), TLS handshake, Finished (20):

SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey

ALPN: server did not agree on a protocol. Uses default.

Server certificate:

subject: C=FR; O=Orange; CN=0827A8-Livebox Nautilus-JA25170AV019345

start date: Jul  1 10:15:01 2025 GMT

expire date: Jul  1 10:15:01 2040 GMT

issuer: C=FR; O=Orange; CN=Livebox Nautilus GW OFR Prod

Certificate level 0: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA256

Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption

Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption

Certificate level 3: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption

Certificate level 4: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384

Certificate level 5: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384

subjectAltName does not match hostname qui.etxea.xubi.org

SSL: no alternative certificate subject name matches target hostname ‘qui.etxea.xubi.org’

closing connection #0curl: (60) SSL: no alternative certificate subject name matches target hostname ‘qui.etxea.xubi.org’More details here: 

curl failed to verify the legitimacy of the server and therefore could notestablish a secure connection to it. To learn more about this situation andhow to fix it, please visit the webpage mentioned above.


```

Assistance disclosure

No AI used.

Looks fine to me:

$ curl -vL https://qui.etxea.xubi.org                                                                
* Host qui.etxea.xubi.org:443 was resolved.
* IPv6: 2a01:cb18:82e1:4000:a27:a8ff:fec5:6386
* IPv4: 92.138.191.247
*   Trying [2a01:cb18:82e1:4000:a27:a8ff:fec5:6386]:443...
* Immediate connect fail for 2a01:cb18:82e1:4000:a27:a8ff:fec5:6386: Network is unreachable
*   Trying 92.138.191.247:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
*   CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*   subject: CN=qui.etxea.xubi.org
*   start date: May 30 00:00:00 2026 GMT
*   expire date: Aug 28 23:59:59 2026 GMT
*   issuer: C=AT; O=ZeroSSL GmbH; CN=ZeroSSL ECC DV SSL CA 2
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
*   Certificate level 1: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   subjectAltName: "qui.etxea.xubi.org" matches cert's "qui.etxea.xubi.org"
* SSL certificate verified via OpenSSL.
* Established connection to qui.etxea.xubi.org (92.138.191.247 port 443) from 192.168.2.108 port 38048 
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://qui.etxea.xubi.org/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: qui.etxea.xubi.org]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.18.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: qui.etxea.xubi.org
> User-Agent: curl/8.18.0
> Accept: */*
> 
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 200 
< alt-svc: h3=":443"; ma=2592000
< content-type: text/html; charset=utf-8
< date: Tue, 02 Jun 2026 14:46:42 GMT
< vary: Accept-Encoding
< vary: Cookie
< via: 1.1 Caddy
< 
<!doctype html>
<html lang="en" class="dark">
...

But anyway, to force Caddy to use zerossl’s API, then instead of using ca (which configures the acme issuer) you may use the zerossl issuer (which has specialized logic to interact with zerossl directly). Like this: tls (Caddyfile directive) — Caddy Documentation

	tls mail@mail.fr {
		issuer zerossl <api_key> {
			...
		}
	}

Hello,

i tried to launch the curl command. The first run is working as it should but if i relaunch it the handshake fails.

*   Trying 92.138.191.247:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_CHACHA20_POLY1305_SHA256 / X25519MLKEM768 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*   subject: CN=qui.etxea.xubi.org
*   start date: May 30 00:00:00 2026 GMT
*   expire date: Aug 28 23:59:59 2026 GMT
*   issuer: C=AT; O=ZeroSSL GmbH; CN=ZeroSSL ECC DV SSL CA 2
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA256
*   Certificate level 1: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 2: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   subjectAltName: "qui.etxea.xubi.org" matches cert's "qui.etxea.xubi.org"
* OpenSSL verify result: 0
* SSL certificate verified via OpenSSL.
* Established connection to qui.etxea.xubi.org (92.138.191.247 port 443) from 192.168.1.11 port 39204
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://qui.etxea.xubi.org/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: qui.etxea.xubi.org]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.20.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: qui.etxea.xubi.org
> User-Agent: curl/8.20.0
> Accept: */*
>
* Request completely sent off
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 200
< alt-svc: h3=":443"; ma=2592000
< content-type: text/html; charset=utf-8
< date: Tue, 02 Jun 2026 16:57:57 GMT
< vary: Accept-Encoding
< vary: Cookie
< via: 1.1 Caddy
<
<!doctype html>
<html lang="en" class="dark">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/x-icon" href="data:," />
    <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <title>qui</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <script>
      /* Anti-FOUC: Apply theme immediately before render */
      (function() {
        try {
          // Check if current theme is lightOnly (e.g., Napster)
          var isLightOnly = localStorage.getItem('theme-light-only') === 'true';

          // Get stored theme preferences
          var themeMode = localStorage.getItem('theme') || 'auto';
          var isDark = !isLightOnly && (themeMode === 'dark' ||
                       (themeMode === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches));

          // Apply dark class if needed
          if (isDark) {
            document.documentElement.classList.add('dark');
          } else {
            document.documentElement.classList.remove('dark');
          }

          // Set immediate background to prevent flash
          // Default to minimal theme dark background
          document.documentElement.style.backgroundColor = isDark ? 'oklch(0.1450 0 0)' : 'oklch(0.98 0 0)';
          document.body && (document.body.style.backgroundColor = isDark ? 'oklch(0.1450 0 0)' : 'oklch(0.98 0 0)');

          // Check if we have stored critical theme vars for more accurate color
          var criticalVars = localStorage.getItem('theme-critical-vars');
          if (criticalVars) {
            try {
              var vars = JSON.parse(criticalVars);
              if (vars.background) {
                document.documentElement.style.backgroundColor = vars.background;
                document.body && (document.body.style.backgroundColor = vars.background);
              }
            } catch(e) {}
          }
        } catch (e) {
          // Fallback to dark theme on error
          document.documentElement.style.backgroundColor = 'oklch(0.1450 0 0)';
        }
      })();
    </script>
    <script type="module" crossorigin src="/assets/index-Cq_mFN-o.js"></script>
    <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-BYbx6iT9.js">
    <link rel="modulepreload" crossorigin href="/assets/react-vendor-DG7TRbss.js">
    <link rel="modulepreload" crossorigin href="/assets/ui-vendor-D_JNHeD8.js">
    <link rel="modulepreload" crossorigin href="/assets/tanstack-DFsWx7kD.js">
    <link rel="stylesheet" crossorigin href="/assets/index-0pkJjmMP.css">
  <link rel="manifest" href="/manifest.webmanifest"><script>window.__QUI_BASE_URL__="/";window.__QUI_VERSION__="v1.19.0";</script></head>
  <body>
    <div id="root"></div>
  </body>
</html>
* Connection #0 to host qui.etxea.xubi.org:443 left intact
[etxea@dell-5420 ~]$ curl -vL https://qui.etxea.xubi.org
*   Trying [2a01:cb18:82e1:4000:a27:a8ff:fec5:6386]:443...
* Host qui.etxea.xubi.org:443 was resolved.
* IPv6: 2a01:cb18:82e1:4000:a27:a8ff:fec5:6386
* IPv4: 92.138.191.247
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*   subject: C=FR; O=Orange; CN=0827A8-Livebox Nautilus-JA25170AV019345
*   start date: Jul  1 10:15:01 2025 GMT
*   expire date: Jul  1 10:15:01 2040 GMT
*   issuer: C=FR; O=Orange; CN=Livebox Nautilus GW OFR Prod
*   Certificate level 0: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA256
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 3: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 4: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 5: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*  subjectAltName does not match hostname qui.etxea.xubi.org
* SSL: no alternative certificate subject name matches target hostname 'qui.etxea.xubi.org'
* closing connection #0
curl: (60) SSL: no alternative certificate subject name matches target hostname 'qui.etxea.xubi.org'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
[etxea@dell-5420 ~]$ curl -vL https://qui.etxea.xubi.org
*   Trying [2a01:cb18:82e1:4000:a27:a8ff:fec5:6386]:443...
* Host qui.etxea.xubi.org:443 was resolved.
* IPv6: 2a01:cb18:82e1:4000:a27:a8ff:fec5:6386
* IPv4: 92.138.191.247
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* SSL Trust Anchors:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Request CERT (13):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Certificate (11):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / x25519 / id-ecPublicKey
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*   subject: C=FR; O=Orange; CN=0827A8-Livebox Nautilus-JA25170AV019345
*   start date: Jul  1 10:15:01 2025 GMT
*   expire date: Jul  1 10:15:01 2040 GMT
*   issuer: C=FR; O=Orange; CN=Livebox Nautilus GW OFR Prod
*   Certificate level 0: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA256
*   Certificate level 1: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 3: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 4: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 5: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using ecdsa-with-SHA384
*  subjectAltName does not match hostname qui.etxea.xubi.org
* SSL: no alternative certificate subject name matches target hostname 'qui.etxea.xubi.org'
* closing connection #0
curl: (60) SSL: no alternative certificate subject name matches target hostname 'qui.etxea.xubi.org'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the webpage mentioned above.
```

I may say something stupid but maybe the router is interfering during the handshake… If I’m understanding correctly the router takes the role of the caddy server providing its certificate.

I think i understood the problem : The router always “shows” its certificates on usual https port (ie 443). Using another port (here 33443) allows me to use https and caddy.

So i rewrite my Caddyfile like that :


{
	servers :33443 {
	   name test
		listener_wrappers {
			http_redirect
			tls
		}
	}

	servers :80 {
		protocols h1 h2c
	}
}
rpi.local {
	tls internal
	redir /jellyfin /jellyfin/
	reverse_proxy /jellyfin/* localhost:8096
	redir /prowlarr /prowlarr/
	reverse_proxy /prowlarr/* localhost:9696
}
kodi.rpi.local {
	tls internal
	reverse_proxy localhost:8585
}
qui.rpi.local {
	tls internal
	reverse_proxy localhost:7476
}
qbt.rpi.local {
	tls internal
	reverse_proxy localhost:8080
}
etxea.xubi.org:33443 {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	redir /jellyfin /jellyfin/
	reverse_proxy /jellyfin/* localhost:8096
	redir /prowlarr /prowlarr/
	reverse_proxy /prowlarr/* localhost:9696
}
kodi.etxea.xubi.org:33443 {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:8585
}
qui.etxea.xubi.org:33443 {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:7476
}
qbt.etxea.xubi.org:33443 {
	tls mail@mail.fr {
		ca https://acme.zerossl.com/v2/DV90
	}
	reverse_proxy localhost:8080
}

Now everything is fine. Please let me know if i misuse servers global options.