1. The problem I’m having:
I want to serve https on multiple ports. I want each site to just use the certificate I choose, regardless of SNI and SAN.
What I instead observe is that when I have multiple sites, calling one site where SAN doesn’t match, but it does match on a different site, will cause a TLS alert.
When 443 has a self-signed cert with SAN DNS:localhost
:
$ openssl s_client -connect localhost:443
Connecting to ::1
CONNECTED(000001AC)
58410000:error:0A000438:SSL routines:ssl3_read_bytes:tlsv1 alert internal error:../openssl-3.2.3/ssl/record/rec_layer_s3.c:909:SSL alert number 80
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 306 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
It works fine if :443 is the only site in my caddyfile or if I add -servername localhost
This behavior depends on the SAN in bar
, in my example the bar certificate has SAN:
DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
If bar
’s SAN didn’t match anything in the request no alert would be raised.
The only workaround I can see is to run multiple caddy instances with a single site block per caddyfile, but I’m hoping there’s some other solution I’ve missed.
2. Error messages and/or full log output:
$ caddy run
2025/02/11 22:47:03.730 INFO using adjacent Caddyfile
2025/02/11 22:47:03.731 INFO adapted config to JSON {"adapter": "caddyfile"}
2025/02/11 22:47:03.736 INFO admin admin endpoint started {"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2025/02/11 22:47:03.736 INFO tls.cache.maintenance started background certificate maintenance {"cache": "0xc00042ee00"}
2025/02/11 22:47:03.736 WARN tls stapling OCSP {"error": "no OCSP stapling for [localhost]: no OCSP server specified in certificate"}
2025/02/11 22:47:03.737 DEBUG events event {"name": "cached_unmanaged_cert", "id": "29d790e1-5751-4ef1-8da2-ae0b2792e3f3", "origin": "tls", "data": {"sans":["localhost"]}}
2025/02/11 22:47:03.737 DEBUG tls.cache added certificate to cache {"subjects": ["localhost"], "expiration": "2027/03/10 00:00:01.000", "managed": false, "issuer_key": "", "hash": "a26052287114a645295f6a1a01bf29320734ac4b65ef0f94c99b7203d6cff74a", "cache_size": 1, "cache_capacity": 10000}
2025/02/11 22:47:03.737 WARN tls stapling OCSP {"error": "no OCSP stapling for [test-cert-self-signed localhost 127.0.0.1 ::1]: no OCSP server specified in certificate"}
2025/02/11 22:47:03.737 DEBUG events event {"name": "cached_unmanaged_cert", "id": "e4dd2247-5ceb-4404-a43a-a7ede6eac8a0", "origin": "tls", "data": {"sans":["test-cert-self-signed","localhost","127.0.0.1","::1"]}}
2025/02/11 22:47:03.737 DEBUG tls.cache added certificate to cache {"subjects": ["test-cert-self-signed", "localhost", "127.0.0.1", "::1"], "expiration": "2026/02/11 22:41:28.000", "managed": false, "issuer_key": "", "hash": "75f3b9a1b7923f83c5af0d3907fe5eef9b67c6cb3bb536441248414f4c66485d", "cache_size": 2, "cache_capacity": 10000}
2025/02/11 22:47:03.737 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {"server_name": "srv0"}
2025/02/11 22:47:03.737 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {"server_name": "srv1"}
2025/02/11 22:47:03.737 DEBUG http.auto_https adjusted config {"tls": {"automation":{"policies":[{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"body":"foo","handler":"static_response"}]}],"tls_connection_policies":[{"certificate_selection":{"any_tag":["cert0"]}}],"automatic_https":{}},"srv1":{"listen":[":8443"],"routes":[{"handle":[{"body":"bar","handler":"static_response"}]}],"tls_connection_policies":[{"certificate_selection":{"any_tag":["cert1"]}}],"automatic_https":{}}}}}
2025/02/11 22:47:03.737 DEBUG http starting server loop {"address": "[::]:443", "tls": true, "http3": false}
2025/02/11 22:47:03.737 INFO http enabling HTTP/3 listener {"addr": ":443"}
2025/02/11 22:47:03.738 INFO http.log server running {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2025/02/11 22:47:03.738 DEBUG http starting server loop {"address": "[::]:8443", "tls": true, "http3": false}
2025/02/11 22:47:03.738 INFO http enabling HTTP/3 listener {"addr": ":8443"}
2025/02/11 22:47:03.738 INFO http.log server running {"name": "srv1", "protocols": ["h1", "h2", "h3"]}
2025/02/11 22:47:03.739 DEBUG http starting server loop {"address": "[::]:80", "tls": false, "http3": false}
2025/02/11 22:47:03.739 WARN http HTTP/2 skipped because it requires TLS {"network": "tcp", "addr": ":80"}
2025/02/11 22:47:03.739 WARN http HTTP/3 skipped because it requires TLS {"network": "tcp", "addr": ":80"}
2025/02/11 22:47:03.739 INFO http.log server running {"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2025/02/11 22:47:03.740 INFO autosaved config (load with --resume flag) {"file": "C:\\Users\\Tratt\\AppData\\Roaming\\Caddy\\autosave.json"}
2025/02/11 22:47:03.740 INFO serving initial configuration
2025/02/11 22:47:03.746 INFO tls storage cleaning happened too recently; skipping for now {"storage": "FileStorage:C:\\Users\\Tratt\\AppData\\Roaming\\Caddy", "instance": "5a974297-e716-43bc-8a37-b6c15de022de", "try_again": "2025/02/12 22:47:03.746", "try_again_in": 86400}
2025/02/11 22:47:03.746 INFO tls finished cleaning storage units
2025/02/11 22:47:06.664 DEBUG events event {"name": "tls_get_certificate", "id": "8b8d93a3-6339-4f76-9a3a-5c19a43d6088", "origin": "tls", "data": {"client_hello":{"CipherSuites":[4866,4867,4865,49196,49200,159,52393,52392,52394,49195,49199,158,49188,49192,107,49187,49191,103,49162,49172,57,49161,49171,51,157,156,61,60,53,47,255],"ServerName":"","SupportedCurves":[29,23,30,25,24,256,257,258,259,260],"SupportedPoints":"AAEC","SignatureSchemes":[1027,1283,1539,2055,2056,2074,2075,2076,2057,2058,2059,2052,2053,2054,1025,1281,1537,771,769,770,1026,1282,1538],"SupportedProtos":null,"SupportedVersions":[772,771],"RemoteAddr":{"IP":"::1","Port":55128,"Zone":""},"LocalAddr":{"IP":"::1","Port":443,"Zone":""}}}}
2025/02/11 22:47:06.664 DEBUG tls.handshake choosing certificate {"identifier": "::1", "num_choices": 1}
2025/02/11 22:47:06.664 DEBUG tls.handshake custom certificate selection results {"error": "no certificates matched custom selection policy", "identifier": "::1", "subjects": [], "managed": false, "issuer_key": "", "hash": ""}
2025/02/11 22:47:06.664 DEBUG tls.handshake no certificate matching TLS ClientHello {"remote_ip": "::1", "remote_port": "55128", "server_name": "", "remote": "[::1]:55128", "identifier": "::1", "cipher_suites": [4866, 4867, 4865, 49196, 49200, 159, 52393, 52392, 52394, 49195, 49199, 158, 49188, 49192, 107, 49187, 49191, 103, 49162, 49172, 57, 49161, 49171, 51, 157, 156, 61, 60, 53, 47, 255], "cert_cache_fill": 0.0002, "load_or_obtain_if_necessary": true, "on_demand": false}
2025/02/11 22:47:06.664 DEBUG http.stdlib http: TLS handshake error from [::1]:55128: no certificate available for '::1'
3. Caddy version:
$ caddy version
v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY=
4. How I installed and ran Caddy:
I downloaded caddy_windows_amd64.exe
, renamed it to caddy.exe
and put it in my PATH
a. System environment:
Windows 10. commands are run in git bash
b. Command:
caddy run
c. Service/unit/compose file:
d. My complete Caddy config:
{
debug
}
:443 {
tls "foo.cert" "foo.key"
respond "foo"
}
:8443 {
tls "bar.cert" "bar.key"
respond "bar"
}