Can't get Caddy reverse proxy for Jellyfin working on my Mac

1. The problem I’m having:

I’m trying to set up a reverse proxy to make my Jellyfin server externally accessible outside my local network. I have a domain name i got on namecheap and have it connected to Cloudflare(DNS only), but couldn’t get that to work and decided to step away from getting the domain set up to hopefully pinpoint the issue (although i would like to eventually get a domain set up). Right now I am just trying to connect via public IP address, and when i run caddy using

caddy start --config Caddyfile

it seems like it runs successfully but when I enter my public IP address i just get a connection timed out page (both on my computer and on my phone disconnected from wi-fi). I tried disabling my firewall as a test and instead of a connection time out page i get “Secure Connection Failed Error code: SSL_ERROR_INTERNAL_ERROR_ALERT”

2. Error messages and/or full log output:

2026/01/23 04:41:47.688 INFO maxprocs: Leaving GOMAXPROCS=20: CPU quota undefined
2026/01/23 04:41:47.688 INFO GOMEMLIMIT is updated {“package”: “github.com/KimMachineGun/automemlimit/memlimit”, “GOMEMLIMIT”: 123695058124, “previous”: 9223372036854775807}
2026/01/23 04:41:47.688 INFO using config from file {“file”: “Caddyfile”}
2026/01/23 04:41:47.690 INFO adapted config to JSON {“adapter”: “caddyfile”}
2026/01/23 04:41:47.691 INFO admin admin endpoint started {“address”: “localhost:2019”, “enforce_origin”: false, “origins”: [“//localhost:2019”, “//[::1]:2019”, “//127.0.0.1:2019”]}
2026/01/23 04:41:47.692 INFO http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {“server_name”: “srv0”, “https_port”: 443}
2026/01/23 04:41:47.692 INFO tls.cache.maintenance started background certificate maintenance {“cache”: “0xc0004f4b80”}
2026/01/23 04:41:47.692 INFO http.auto_https enabling automatic HTTP->HTTPS redirects {“server_name”: “srv0”}
2026/01/23 04:41:47.692 DEBUG http.auto_https adjusted config {“tls”: {“automation”:{“policies”:[{“subjects”:[“107.220.81.150”]},{}]}}, “http”: {“servers”:{“remaining_auto_https_redirects”:{“listen”:[“:80”],“routes”:[{},{}]},“srv0”:{“listen”:[“:443”],“routes”:[{“handle”:[{“handler”:“subroute”,“routes”:[{“handle”:[{“handler”:“static_response”,“status_code”:403}],“match”:[{“path”:[“/metrics*”]}]},{“handle”:[{“handler”:“reverse_proxy”,“upstreams”:[{“dial”:“127.0.0.1:8096”}]}]}]}],“terminal”:true}],“tls_connection_policies”:[{}],“automatic_https”:{}}}}}
2026/01/23 04:41:47.693 DEBUG http starting server loop {“address”: “[::]:443”, “tls”: true, “http3”: false}
2026/01/23 04:41:47.693 INFO http enabling HTTP/3 listener {“addr”: “:443”}
2026/01/23 04:41:47.693 INFO http.log server running {“name”: “srv0”, “protocols”: [“h1”, “h2”, “h3”]}
2026/01/23 04:41:47.693 DEBUG http starting server loop {“address”: “[::]:80”, “tls”: false, “http3”: false}
2026/01/23 04:41:47.693 WARN http HTTP/2 skipped because it requires TLS {“network”: “tcp”, “addr”: “:80”}
2026/01/23 04:41:47.693 WARN http HTTP/3 skipped because it requires TLS {“network”: “tcp”, “addr”: “:80”}
2026/01/23 04:41:47.693 INFO http.log server running {“name”: “remaining_auto_https_redirects”, “protocols”: [“h1”, “h2”, “h3”]}
2026/01/23 04:41:47.693 INFO http enabling automatic TLS certificate management {“domains”: [“107.220.81.150”]}
2026/01/23 04:41:47.693 WARN tls stapling OCSP {“identifiers”: [“107.220.81.150”]}
2026/01/23 04:41:47.693 DEBUG tls.cache added certificate to cache {“subjects”: [“107.220.81.150”], “expiration”: “2026/01/23 12:29:54.000”, “managed”: true, “issuer_key”: “local”, “hash”: “d722e8858cbd97818e0042e5e5697e2b48a18b9cd80fc74d5395c3bb197b2216”, “cache_size”: 1, “cache_capacity”: 10000}
2026/01/23 04:41:47.693 DEBUG events event {“name”: “cached_managed_cert”, “id”: “81d8fc34-a6d1-4d9f-a53b-cf2b6263e61e”, “origin”: “tls”, “data”: {“sans”:[“107.220.81.150”]}}
2026/01/23 04:41:47.696 INFO pki.ca.local root certificate is already trusted by system {“path”: “storage:pki/authorities/local/root.crt”}
2026/01/23 04:41:47.696 DEBUG events event {“name”: “started”, “id”: “84e10ba4-a3a9-4d23-835b-23e4cce6fb50”, “origin”: “”, “data”: null}
2026/01/23 04:41:47.713 INFO tls storage cleaning happened too recently; skipping for now {“storage”: “FileStorage:/Users/gribo/Library/Application Support/Caddy”, “instance”: “94566520-5ad5-4777-894f-46fb38c06b13”, “try_again”: “2026/01/24 04:41:47.713”, “try_again_in”: 86399.999999679}
2026/01/23 04:41:47.714 INFO tls finished cleaning storage units
2026/01/23 04:41:47.714 INFO autosaved config (load with --resume flag) {“file”: “/Users/gribo/Library/Application Support/Caddy/autosave.json”}
2026/01/23 04:41:47.714 INFO serving initial configuration
Successfully started Caddy (pid=22563) - Caddy is running in the background

 

3. Caddy version:

caddy version v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=

4. How I installed and ran Caddy:

Through terminal

brew install caddy

a. System environment:

2017 MacOS Sequioia 15.7, not using docker, using terminal, I started using bash but updated to Zsh and started over

b. Command:

caddy start --config Caddyfile

d. My complete Caddy config:

{
debug
}
107.220.81.150 {
@metrics {
path /metrics*
}
respond @metrics 403
reverse_proxy 127.0.0.1:8096
}

5. Links to relevant resources:

Guide I’ve been following to try and get Jellyfin and caddy set up on Mac GitHub - Digital-Shane/jellyfin-on-macos: Hosting Jellyfin on macOS. The Ultimate Guide.

Would be interesting to see what a curl -v might reveal in such a case. Maybe also OpenSSL.

curl -v 107.220.81.150
openssl s_client -connect 107.220.81.150:443

tried curl -v 107.220.81.150 with firewall on and got

 Trying 107.220.81.150:80...
* Connected to 107.220.81.150 (107.220.81.150) port 80
> GET / HTTP/1.1
> Host: 107.220.81.150
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
* Recv failure: Connection reset by peer
* Closing connection
curl: (56) Recv failure: Connection reset by peer

then tried it again with firewall off and got

Trying 107.220.81.150:80...
* Connected to 107.220.81.150 (107.220.81.150) port 80
> GET / HTTP/1.1
> Host: 107.220.81.150
> User-Agent: curl/8.7.1
> Accept: */*
> 
* Request completely sent off
< HTTP/1.1 308 Permanent Redirect
< Connection: close
< Location: https://107.220.81.150/
< Server: Caddy
< Date: Fri, 23 Jan 2026 18:22:36 GMT
< Content-Length: 0
< 
* Closing connection

tried openssl s_client -connect 107.220.81.150:443 and got

CONNECTED(00000003)
140704584609792:error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error:/AppleInternal/Library/BuildRoots/4~B5vGugAacBqx1Rv6ZIrYmRaiDWoWCZgo6c_MR5I/Library/Caches/com.apple.xbs/Sources/libressl/libressl-3.3/ssl/tls13_lib.c:129:SSL alert number 80
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 287 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : 0000
    Session-ID: 
    Session-ID-ctx: 
    Master-Key: 
    Start Time: 1769192944
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
---

And here is my caddy start --config Caddyfile log left running in a separate terminal window

maxprocs: Leaving GOMAXPROCS=20: CPU quota undefined
2026/01/23 18:20:41.765	INFO	GOMEMLIMIT is updated	{"package": "github.com/KimMachineGun/automemlimit/memlimit", "GOMEMLIMIT": 123695058124, "previous": 9223372036854775807}
2026/01/23 18:20:41.766	INFO	using config from file	{"file": "Caddyfile"}
2026/01/23 18:20:41.767	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2026/01/23 18:20:41.768	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2026/01/23 18:20:41.768	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2026/01/23 18:20:41.768	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2026/01/23 18:20:41.768	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc000422180"}
2026/01/23 18:20:41.771	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["107.220.81.150"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"static_response","status_code":403}],"match":[{"path":["/metrics*"]}]},{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"127.0.0.1:8096"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
2026/01/23 18:20:41.771	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2026/01/23 18:20:41.771	WARN	http	HTTP/2 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 18:20:41.771	WARN	http	HTTP/3 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 18:20:41.771	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2026/01/23 18:20:41.771	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": false}
2026/01/23 18:20:41.771	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2026/01/23 18:20:41.771	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2026/01/23 18:20:41.771	INFO	http	enabling automatic TLS certificate management	{"domains": ["107.220.81.150"]}
2026/01/23 18:20:41.772	WARN	tls	stapling OCSP	{"identifiers": ["107.220.81.150"]}
2026/01/23 18:20:41.773	DEBUG	tls.cache	added certificate to cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/23 12:29:54.000", "managed": true, "issuer_key": "local", "hash": "d722e8858cbd97818e0042e5e5697e2b48a18b9cd80fc74d5395c3bb197b2216", "cache_size": 1, "cache_capacity": 10000}
2026/01/23 18:20:41.773	DEBUG	events	event	{"name": "cached_managed_cert", "id": "0113037f-cc17-4f7d-ad44-3b816c344b97", "origin": "tls", "data": {"sans":["107.220.81.150"]}}
2026/01/23 18:20:41.773	INFO	tls	certificate is in configured renewal window based on expiration date	{"subjects": [], "expiration": "2026/01/23 12:29:54.000", "ari_cert_id": "", "next_ari_update": null, "renew_check_interval": 600, "window_start": "0001/01/01 00:00:00.000", "window_end": "0001/01/01 00:00:00.000", "remaining": -21047.773157}
2026/01/23 18:20:41.776	INFO	pki.ca.local	root certificate is already trusted by system	{"path": "storage:pki/authorities/local/root.crt"}
2026/01/23 18:20:41.776	DEBUG	events	event	{"name": "started", "id": "b539843f-6b9d-4716-8cda-0a994138bdab", "origin": "", "data": null}
2026/01/23 18:20:41.810	INFO	tls.renew	acquiring lock	{"identifier": "107.220.81.150"}
2026/01/23 18:20:41.810	INFO	autosaved config (load with --resume flag)	{"file": "/Users/gribo/Library/Application Support/Caddy/autosave.json"}
2026/01/23 18:20:41.811	INFO	serving initial configuration
2026/01/23 18:20:41.811	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/Users/gribo/Library/Application Support/Caddy", "instance": "94566520-5ad5-4777-894f-46fb38c06b13", "try_again": "2026/01/24 18:20:41.811", "try_again_in": 86399.999999609}
Successfully started Caddy (pid=2166) - Caddy is running in the background
2026/01/23 18:20:41.811	INFO	tls	finished cleaning storage units
tinodimperio@Tinos-iMac-Pro ~ % 2026/01/23 18:20:41.829	INFO	tls.renew	lock acquired	{"identifier": "107.220.81.150"}
2026/01/23 18:20:41.829	INFO	tls.renew	renewing certificate	{"identifier": "107.220.81.150", "remaining": -21047.829923}
2026/01/23 18:20:41.829	DEBUG	events	event	{"name": "cert_obtaining", "id": "bad8e701-7ef2-4a21-92fd-6826d3f300dd", "origin": "tls", "data": {"forced":false,"identifier":"107.220.81.150","issuer":"local","remaining":-21047829923000,"renewal":true}}
2026/01/23 18:20:41.830	DEBUG	tls	created CSR	{"identifiers": ["107.220.81.150"], "san_dns_names": [], "san_emails": [], "common_name": "", "extra_extensions": 0}
2026/01/23 18:20:41.830	DEBUG	pki.ca.local	using intermediate signer	{"serial": "484098142444138216577113991834761110", "not_before": "2026-01-21 21:11:22 +0000 UTC", "not_after": "2026-01-28 21:11:22 +0000 UTC"}
2026/01/23 18:20:41.888	INFO	tls.renew	certificate renewed successfull{"identifier": "107.220.81.150", "issuer": "local"}
2026/01/23 18:20:41.888	DEBUG	events	event	{"name": "cert_obtained", "id": "57cd69c5-e1a1-480c-9d97-5df12224e3f3", "origin": "tls", "data": {"certificate_path":"certificates/local/107.220.81.150/107.220.81.150.crt","csr_pem":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlIZE1JR0VBZ0VBTUFBd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFSRjdvT0tUdTlISUtDUApacG1LL005eTUxNmVlRFI2eHdFNURMN1hyQ2NLaEdCNmREMnlFWWNNRU0wTnpkVHhNSXRSY3N4V1QrMEtCMXplClNSNWxPLzByb0NJd0lBWUpLb1pJaHZjTkFRa09NUk13RVRBUEJnTlZIUkVFQ0RBR2h3UnIzRkdXTUFvR0NDcUcKU000OUJBTUNBMGdBTUVVQ0lRRE1vQ3hDckt3bTlRZU5JbW5MRll0eUpWd1k4WTJxYk1ZMVdycHdvUTR1TXdJZwpZaVZva0hWaENtUVZQcFlxbUR1Ry9BZGE3dTIyV0tFb2lMRzZIaWI4U0c4PQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K","identifier":"107.220.81.150","issuer":"local","metadata_path":"certificates/local/107.220.81.150/107.220.81.150.json","private_key_path":"certificates/local/107.220.81.150/107.220.81.150.key","remaining":-21047829923000,"renewal":true,"storage_path":"certificates/local/107.220.81.150"}}
2026/01/23 18:20:41.888	INFO	tls.renew	releasing lock	{"identifier": "107.220.81.150"}
2026/01/23 18:20:41.888	INFO	tls	reloading managed certificate	{"identifiers": ["107.220.81.150"]}
2026/01/23 18:20:41.888	WARN	tls	stapling OCSP	{"identifiers": ["107.220.81.150"]}
2026/01/23 18:20:41.888	DEBUG	tls.cache	removed certificate from cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/23 12:29:54.000", "managed": true, "issuer_key": "local", "hash": "d722e8858cbd97818e0042e5e5697e2b48a18b9cd80fc74d5395c3bb197b2216", "cache_size": 0, "cache_capacity": 10000}
2026/01/23 18:20:41.888	DEBUG	tls.cache	added certificate to cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/24 06:20:42.000", "managed": true, "issuer_key": "local", "hash": "c73fff4dc5072caae1715837175e4f6225c22e6a5a80ef437ce580927e1567a0", "cache_size": 1, "cache_capacity": 10000}
2026/01/23 18:20:41.889	INFO	tls.cache	replaced certificate in cache	{"subjects": ["107.220.81.150"], "new_expiration": "2026/01/24 06:20:42.000"}
2026/01/23 18:23:22.533	DEBUG	events	event	{"name": "tls_get_certificate", "id": "2d09124c-3713-4657-bb15-2a7b5d84d5a8", "origin": "tls", "data": {"client_hello":{"CipherSuites":[4865,4867,4866,49195,49199,52393,52392,49196,49200,49162,49161,49171,49172,156,157,47,53],"ServerName":"","SupportedCurves":[4588,29,23,24,25,256,257],"SupportedPoints":"AA==","SignatureSchemes":[1027,1283,1539,2052,2053,2054,1025,1281,1537,515,513],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"RemoteAddr":{"IP":"192.168.1.254","Port":443,"Zone":""},"LocalAddr":{"IP":"192.168.1.64","Port":443,"Zone":""}}}}
2026/01/23 18:23:22.533	DEBUG	tls.handshake	no matching certificates and no custom selection logic	{"identifier": "192.168.1.64"}
2026/01/23 18:23:22.533	DEBUG	tls.handshake	no certificate matching TLS ClientHello	{"remote_ip": "192.168.1.254", "remote_port": "443", "server_name": "", "remote": "192.168.1.254:443", "identifier": "192.168.1.64", "cipher_suites": [4865, 4867, 4866, 49195, 49199, 52393, 52392, 49196, 49200, 49162, 49161, 49171, 49172, 156, 157, 47, 53], "cert_cache_fill": 0.0001, "load_or_obtain_if_necessary": true, "on_demand": false}
2026/01/23 18:23:22.533	DEBUG	http.stdlib	http: TLS handshake error from 192.168.1.254:443: no certificate available for '192.168.1.64'
2026/01/23 18:23:57.539	DEBUG	events	event	{"name": "tls_get_certificate", "id": "fabe2a3a-bb42-4f84-b17b-cd2fc8d11607", "origin": "tls", "data": {"client_hello":{"CipherSuites":[4865,4867,4866,49195,49199,52393,52392,49196,49200,49162,49161,49171,49172,156,157,47,53],"ServerName":"","SupportedCurves":[29,23,24,25,256,257],"SupportedPoints":"AA==","SignatureSchemes":[1027,1283,1539,2052,2053,2054,1025,1281,1537,515,513],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"RemoteAddr":{"IP":"192.168.1.254","Port":443,"Zone":""},"LocalAddr":{"IP":"192.168.1.64","Port":443,"Zone":""}}}}
2026/01/23 18:23:57.539	DEBUG	tls.handshake	no matching certificates and no custom selection logic	{"identifier": "192.168.1.64"}
2026/01/23 18:23:57.539	DEBUG	tls.handshake	no certificate matching TLS ClientHello	{"remote_ip": "192.168.1.254", "remote_port": "443", "server_name": "", "remote": "192.168.1.254:443", "identifier": "192.168.1.64", "cipher_suites": [4865, 4867, 4866, 49195, 49199, 52393, 52392, 49196, 49200, 49162, 49161, 49171, 49172, 156, 157, 47, 53], "cert_cache_fill": 0.0001, "load_or_obtain_if_necessary": true, "on_demand": false}
2026/01/23 18:23:57.539	DEBUG	http.stdlib	http: TLS handshake error from 192.168.1.254:443: no certificate available for '192.168.1.64'
2026/01/23 18:24:29.794	DEBUG	events	event	{"name": "tls_get_certificate", "id": "db84a48c-64d5-408e-a7aa-9eea5c5e584f", "origin": "tls", "data": {"client_hello":{"CipherSuites":[4865,4867,4866,49195,49199,52393,52392,49196,49200,49162,49161,49171,49172,156,157,47,53],"ServerName":"","SupportedCurves":[29,23,24,25,256,257],"SupportedPoints":"AA==","SignatureSchemes":[1027,1283,1539,2052,2053,2054,1025,1281,1537,515,513],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[772,771],"RemoteAddr":{"IP":"192.168.1.254","Port":443,"Zone":""},"LocalAddr":{"IP":"192.168.1.64","Port":443,"Zone":""}}}}
2026/01/23 18:24:29.794	DEBUG	tls.handshake	no matching certificates and no custom selection logic	{"identifier": "192.168.1.64"}
2026/01/23 18:24:29.794	DEBUG	tls.handshake	no certificate matching TLS ClientHello	{"remote_ip": "192.168.1.254", "remote_port": "443", "server_name": "", "remote": "192.168.1.254:443", "identifier": "192.168.1.64", "cipher_suites": [4865, 4867, 4866, 49195, 49199, 52393, 52392, 49196, 49200, 49162, 49161, 49171, 49172, 156, 157, 47, 53], "cert_cache_fill": 0.0001, "load_or_obtain_if_necessary": true, "on_demand": false}
2026/01/23 18:24:29.794	DEBUG	http.stdlib	http: TLS handshake error from 192.168.1.254:443: no certificate available for '192.168.1.64'
2026/01/23 18:29:04.319	DEBUG	events	event	{"name": "tls_get_certificate", "id": "083b2401-e834-4e20-ad83-aa486c899184", "origin": "tls", "data": {"client_hello":{"CipherSuites":[4866,4867,4865,49200,49196,49192,49188,49172,49162,159,107,57,52393,52392,52394,65413,196,136,129,157,61,53,192,132,49199,49195,49191,49187,49171,49161,158,103,51,190,69,156,60,47,186,65,49169,49159,5,4,49170,49160,22,10,255],"ServerName":"","SupportedCurves":[29,23,24,25],"SupportedPoints":"AA==","SignatureSchemes":[2054,1537,1539,2053,1281,1283,2052,1025,1027,513,515],"SupportedProtos":null,"SupportedVersions":[772,771,770,769],"RemoteAddr":{"IP":"192.168.1.254","Port":443,"Zone":""},"LocalAddr":{"IP":"192.168.1.64","Port":443,"Zone":""}}}}
2026/01/23 18:29:04.319	DEBUG	tls.handshake	no matching certificates and no custom selection logic	{"identifier": "192.168.1.64"}
2026/01/23 18:29:04.319	DEBUG	tls.handshake	no certificate matching TLS ClientHello	{"remote_ip": "192.168.1.254", "remote_port": "443", "server_name": "", "remote": "192.168.1.254:443", "identifier": "192.168.1.64", "cipher_suites": [4866, 4867, 4865, 49200, 49196, 49192, 49188, 49172, 49162, 159, 107, 57, 52393, 52392, 52394, 65413, 196, 136, 129, 157, 61, 53, 192, 132, 49199, 49195, 49191, 49187, 49171, 49161, 158, 103, 51, 190, 69, 156, 60, 47, 186, 65, 49169, 49159, 5, 4, 49170, 49160, 22, 10, 255], "cert_cache_fill": 0.0001, "load_or_obtain_if_necessary": true, "on_demand": false}
2026/01/23 18:29:04.319	DEBUG	http.stdlib	http: TLS handshake error from 192.168.1.254:443: no certificate available for '192.168.1.64'


Thanks for the reply by the way! I am a a noob at this stuff, but been trying to learn as much as I can over the past few weeks, I appreciate the help!

I recreated your config locally, observed the same behavior and after some research found out that this comes up more or less regularly, example.

2026/01/23 18:23:22.533	DEBUG	http.stdlib	http: TLS handshake error from 192.168.1.254:443: no certificate available for '192.168.1.64'

I found this line peculiar. Why would browsing to my public IPv4 (implying this to be the sent SNI) match my local IPv4 address? Could be because my local IPv4 actually is 192.168.x.x. The definitive, permanent solution would be to simply use some form of DNS name here and have Caddy issue a cert to this name (it would make it possible to use publically trusted certs), but if you truly insist on IP addresses, add your local IPv4 to the site addresses.

107.220.81.150 192.168.1.64 {
  …
}

By the way, the @metrics definition is superfluous if you only use a path match. You can use /metrics* as the matcher in response directly.

respond /metrics* 403

Caddy seems to be listening on your IP and even issuing a certificate for it:

$ openssl s_client -servername 107.220.81.150 -connect 107.220.81.150:443 </dev/null 2>/dev/null | openssl x509 -noout -text 2>/dev/null
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            45:b8:ef:30:5b:93:06:ce:61:e7:d9:b1:96:dd:9b:69
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=Caddy Local Authority - ECC Intermediate
        Validity
            Not Before: Jan 23 18:20:41 2026 GMT
            Not After : Jan 24 06:20:41 2026 GMT
        Subject:
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:45:ee:83:8a:4e:ef:47:20:a0:8f:66:99:8a:fc:
                    cf:72:e7:5e:9e:78:34:7a:c7:01:39:0c:be:d7:ac:
                    27:0a:84:60:7a:74:3d:b2:11:87:0c:10:cd:0d:cd:
                    d4:f1:30:8b:51:72:cc:56:4f:ed:0a:07:5c:de:49:
                    1e:65:3b:fd:2b
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Subject Key Identifier:
                4D:DE:52:B4:E0:3F:83:26:6E:02:7E:09:EB:DC:AA:7D:F8:9A:4C:5E
            X509v3 Authority Key Identifier:
                19:62:32:28:30:95:7A:25:52:9E:F0:DE:60:4A:0A:1D:4B:76:1A:6F
            X509v3 Subject Alternative Name: critical
                IP Address:107.220.81.150
    Signature Algorithm: ecdsa-with-SHA256
    Signature Value:
        30:45:02:21:00:85:02:83:9c:34:f8:87:f8:96:7f:2c:1a:c8:
        ca:bf:30:5a:7c:f3:34:5d:d5:a1:8b:ab:e7:17:ed:2b:6a:4e:
        f2:02:20:7b:5d:d8:7c:d2:9a:b7:f1:74:62:a2:aa:49:8f:be:
        8a:89:0e:1c:3a:54:78:a9:14:b7:2a:7a:51:ff:76:bb:a8

Check out these lines in particular:

...
        Issuer: CN=Caddy Local Authority - ECC Intermediate
...
            X509v3 Subject Alternative Name: critical
                IP Address:107.220.81.150
...

Could you try this config for me?

{
    debug
}

107.220.81.150 {
    respond ""Alive!"
}

Then run this and let me know what you get:

curl -kv https://107.220.81.150

Tried adding the local IP to the Caddyfile like this:

{
	debug
}

107.220.81.150 192.168.1.64 {
	@metrics {
		path /metrics*
	}
	respond @metrics 403

	reverse_proxy 127.0.0.1:8096
}

Ran caddy and tried connecting from my computer and phone to the public IP and got request timed out on my phone and got Secure Connection Failed, Error code: SSL_ERROR_INTERNAL_ERROR_ALERT on my computer

Here’s the log in terminal

2026/01/23 21:10:33.338	INFO	maxprocs: Leaving GOMAXPROCS=20: CPU quota undefined
2026/01/23 21:10:33.338	INFO	GOMEMLIMIT is updated	{"package": "github.com/KimMachineGun/automemlimit/memlimit", "GOMEMLIMIT": 123695058124, "previous": 9223372036854775807}
2026/01/23 21:10:33.338	INFO	using config from file	{"file": "Caddyfile"}
2026/01/23 21:10:33.339	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2026/01/23 21:10:33.340	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2026/01/23 21:10:33.340	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2026/01/23 21:10:33.340	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2026/01/23 21:10:33.340	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc0007a0a80"}
2026/01/23 21:10:33.341	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["107.220.81.150","192.168.1.64"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"static_response","status_code":403}],"match":[{"path":["/metrics*"]}]},{"handle":[{"handler":"reverse_proxy","upstreams":[{"dial":"127.0.0.1:8096"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
2026/01/23 21:10:33.341	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": false}
2026/01/23 21:10:33.341	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2026/01/23 21:10:33.342	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:10:33.342	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2026/01/23 21:10:33.342	WARN	http	HTTP/2 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:10:33.342	WARN	http	HTTP/3 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:10:33.342	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:10:33.342	INFO	http	enabling automatic TLS certificate management	{"domains": ["107.220.81.150", "192.168.1.64"]}
2026/01/23 21:10:33.342	WARN	tls	stapling OCSP	{"identifiers": ["192.168.1.64"]}
2026/01/23 21:10:33.342	DEBUG	tls.cache	added certificate to cache	{"subjects": ["192.168.1.64"], "expiration": "2026/01/24 09:08:56.000", "managed": true, "issuer_key": "local", "hash": "bc5ee43cdcdfb4c1a15955c401b1efb83cd082b3ad01cc72ca00f08c89b1b326", "cache_size": 1, "cache_capacity": 10000}
2026/01/23 21:10:33.342	DEBUG	events	event	{"name": "cached_managed_cert", "id": "84d3fbb4-f32b-4bdc-9032-eb3d510065a1", "origin": "tls", "data": {"sans":["192.168.1.64"]}}
2026/01/23 21:10:33.342	WARN	tls	stapling OCSP	{"identifiers": ["107.220.81.150"]}
2026/01/23 21:10:33.342	DEBUG	tls.cache	added certificate to cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/24 06:20:42.000", "managed": true, "issuer_key": "local", "hash": "c73fff4dc5072caae1715837175e4f6225c22e6a5a80ef437ce580927e1567a0", "cache_size": 2, "cache_capacity": 10000}
2026/01/23 21:10:33.342	DEBUG	events	event	{"name": "cached_managed_cert", "id": "c2afebd4-e852-439a-b0e4-c5fb3268360c", "origin": "tls", "data": {"sans":["107.220.81.150"]}}
2026/01/23 21:10:33.346	INFO	pki.ca.local	root certificate is already trusted by system	{"path": "storage:pki/authorities/local/root.crt"}
2026/01/23 21:10:33.346	DEBUG	events	event	{"name": "started", "id": "0437f4b6-d916-44bd-a039-41bf25420c27", "origin": "", "data": null}
2026/01/23 21:10:33.346	INFO	autosaved config (load with --resume flag)	{"file": "/Users/gribo/Library/Application Support/Caddy/autosave.json"}
2026/01/23 21:10:33.346	INFO	serving initial configuration
Successfully started Caddy (pid=7959) - Caddy is running in the background
gribo@gribos-iMac-Pro ~ % 2026/01/23 21:10:33.366	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/Users/gribo/Library/Application Support/Caddy", "instance": "94566520-5ad5-4777-894f-46fb38c06b13", "try_again": "2026/01/24 21:10:33.366", "try_again_in": 86399.999999655}
2026/01/23 21:10:33.367	INFO	tls	finished cleaning storage units

And for you’re note about metrics, is this better? i’ll update the Caddyfile like so

{
	debug
}

107.220.81.150 192.168.1.64 {
	
	respond /metrics* 403

	reverse_proxy 127.0.0.1:8096
}

Then try this one as well:

{
	debug
}

107.220.81.150 {
	respond /metrics* 403
	reverse_proxy 127.0.0.1:8096 {
		header_up Host {upstream_hostport}
	}
}

Since you’re using an IP-based host (107.220.81.150), Caddy is probably sending Host: 107.220.81.150 to your 127.0.0.1:8096 backend.

I tested this setup on my laptop and it worked fine:

{
	debug
}

192.168.139.3 {
	respond /metrics* 403
	reverse_proxy 127.0.0.1:8096 {
		header_up Host {upstream_hostport}
	}
}

# Simulated backend
http://127.0.0.1:8096 {
	respond "UPSTREAM"
}

Results:

$ curl https://192.168.139.3
UPSTREAM

$ curl https://192.168.139.3/metrics -I
HTTP/2 403
alt-svc: h3=":443"; ma=2592000
server: Caddy
date: Fri, 23 Jan 2026 21:28:52 GMT

Tried that config in my Caddyfile and used

curl -kv https://107.220.81.150

i got

*   Trying 107.220.81.150:443...
* Connected to 107.220.81.150 (107.220.81.150) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error
* Closing connection
curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error

Tried

{
	debug
}

107.220.81.150 {
	respond /metrics* 403
	reverse_proxy 127.0.0.1:8096 {
		header_up Host {upstream_hostport}
	}
}

Receiving same Error code: SSL_ERROR_INTERNAL_ERROR_ALERT when i type 107.220.81.150 into browser

Here’s the log after running it

maxprocs: Leaving GOMAXPROCS=20: CPU quota undefined
2026/01/23 21:40:14.835	INFO	GOMEMLIMIT is updated	{"package": "github.com/KimMachineGun/automemlimit/memlimit", "GOMEMLIMIT": 123695058124, "previous": 9223372036854775807}
2026/01/23 21:40:14.836	INFO	using config from file	{"file": "Caddyfile"}
2026/01/23 21:40:14.836	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2026/01/23 21:40:14.836	WARN	Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies	{"adapter": "caddyfile", "file": "Caddyfile", "line": 10}
2026/01/23 21:40:14.838	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2026/01/23 21:40:14.838	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2026/01/23 21:40:14.838	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc000712080"}
2026/01/23 21:40:14.838	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2026/01/23 21:40:14.839	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["107.220.81.150"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"static_response","status_code":403}],"match":[{"path":["/metrics*"]}]},{"handle":[{"handler":"reverse_proxy","headers":{"request":{"set":{"Host":["{http.reverse_proxy.upstream.hostport}"]}}},"upstreams":[{"dial":"127.0.0.1:8096"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}}}}}
2026/01/23 21:40:14.839	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": false}
2026/01/23 21:40:14.839	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2026/01/23 21:40:14.839	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:40:14.840	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2026/01/23 21:40:14.840	WARN	http	HTTP/2 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:40:14.840	WARN	http	HTTP/3 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:40:14.840	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:40:14.840	INFO	http	enabling automatic TLS certificate management	{"domains": ["107.220.81.150"]}
2026/01/23 21:40:14.840	WARN	tls	stapling OCSP	{"identifiers": ["107.220.81.150"]}
2026/01/23 21:40:14.840	DEBUG	tls.cache	added certificate to cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/24 06:20:42.000", "managed": true, "issuer_key": "local", "hash": "c73fff4dc5072caae1715837175e4f6225c22e6a5a80ef437ce580927e1567a0", "cache_size": 1, "cache_capacity": 10000}
2026/01/23 21:40:14.840	DEBUG	events	event	{"name": "cached_managed_cert", "id": "7ad6d306-49fc-4762-84c9-7adb364cd99e", "origin": "tls", "data": {"sans":["107.220.81.150"]}}
2026/01/23 21:40:14.843	INFO	pki.ca.local	root certificate is already trusted by system	{"path": "storage:pki/authorities/local/root.crt"}
2026/01/23 21:40:14.843	DEBUG	events	event	{"name": "started", "id": "aff96c40-8a0e-49ab-bd4d-945ed1a75e64", "origin": "", "data": null}
2026/01/23 21:40:14.862	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/Users/gribo/Library/Application Support/Caddy", "instance": "94566520-5ad5-4777-894f-46fb38c06b13", "try_again": "2026/01/24 21:40:14.862", "try_again_in": 86399.999999612}
2026/01/23 21:40:14.862	INFO	autosaved config (load with --resume flag)	{"file": "/Users/gribo/Library/Application Support/Caddy/autosave.json"}
2026/01/23 21:40:14.862	INFO	serving initial configuration
2026/01/23 21:40:14.862	INFO	tls	finished cleaning storage units
Successfully started Caddy (pid=8887) - Caddy is running in the background

Tried putting this in my caddyfile to match yours

{
	debug
}

107.220.81.150 {
	respond /metrics* 403
	reverse_proxy 127.0.0.1:8096 {
		header_up Host {upstream_hostport}
	}
}

# Simulated backend
http://127.0.0.1:8096 {
	respond "UPSTREAM"
}

entered caddy start --config Caddyfile and here’s the log

2026/01/23 21:51:36.617	INFO	maxprocs: Leaving GOMAXPROCS=20: CPU quota undefined
2026/01/23 21:51:36.617	INFO	GOMEMLIMIT is updated	{"package": "github.com/KimMachineGun/automemlimit/memlimit", "GOMEMLIMIT": 123695058124, "previous": 9223372036854775807}
2026/01/23 21:51:36.617	INFO	using config from file	{"file": "Caddyfile"}
2026/01/23 21:51:36.618	INFO	adapted config to JSON	{"adapter": "caddyfile"}
2026/01/23 21:51:36.618	WARN	Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies	{"adapter": "caddyfile", "file": "Caddyfile", "line": 15}
2026/01/23 21:51:36.620	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2026/01/23 21:51:36.621	INFO	http.auto_https	server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS	{"server_name": "srv0", "https_port": 443}
2026/01/23 21:51:36.621	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc000626600"}
2026/01/23 21:51:36.621	INFO	http.auto_https	enabling automatic HTTP->HTTPS redirects	{"server_name": "srv0"}
2026/01/23 21:51:36.621	DEBUG	http.auto_https	adjusted config	{"tls": {"automation":{"policies":[{"subjects":["107.220.81.150"]},{}]}}, "http": {"servers":{"remaining_auto_https_redirects":{"listen":[":80"],"routes":[{},{}]},"srv0":{"listen":[":443"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"handler":"static_response","status_code":403}],"match":[{"path":["/metrics*"]}]},{"handle":[{"handler":"reverse_proxy","headers":{"request":{"set":{"Host":["{http.reverse_proxy.upstream.hostport}"]}}},"upstreams":[{"dial":"127.0.0.1:8096"}]}]}]}],"terminal":true}],"tls_connection_policies":[{}],"automatic_https":{}},"srv1":{"listen":[":8096"],"routes":[{"handle":[{"handler":"subroute","routes":[{"handle":[{"body":"UPSTREAM","handler":"static_response"}]}]}],"terminal":true}],"automatic_https":{"skip":["127.0.0.1"]}}}}}
2026/01/23 21:51:36.622	DEBUG	http	starting server loop	{"address": "[::]:443", "tls": true, "http3": false}
2026/01/23 21:51:36.622	INFO	http	enabling HTTP/3 listener	{"addr": ":443"}
2026/01/23 21:51:36.622	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:51:36.622	DEBUG	http	starting server loop	{"address": "[::]:8096", "tls": false, "http3": false}
2026/01/23 21:51:36.622	WARN	http	HTTP/2 skipped because it requires TLS	{"network": "tcp", "addr": ":8096"}
2026/01/23 21:51:36.622	WARN	http	HTTP/3 skipped because it requires TLS	{"network": "tcp", "addr": ":8096"}
2026/01/23 21:51:36.622	INFO	http.log	server running	{"name": "srv1", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:51:36.622	DEBUG	http	starting server loop	{"address": "[::]:80", "tls": false, "http3": false}
2026/01/23 21:51:36.622	WARN	http	HTTP/2 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:51:36.622	WARN	http	HTTP/3 skipped because it requires TLS	{"network": "tcp", "addr": ":80"}
2026/01/23 21:51:36.622	INFO	http.log	server running	{"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2026/01/23 21:51:36.622	INFO	http	enabling automatic TLS certificate management	{"domains": ["107.220.81.150"]}
2026/01/23 21:51:36.622	WARN	tls	stapling OCSP	{"identifiers": ["107.220.81.150"]}
2026/01/23 21:51:36.622	DEBUG	tls.cache	added certificate to cache	{"subjects": ["107.220.81.150"], "expiration": "2026/01/24 06:20:42.000", "managed": true, "issuer_key": "local", "hash": "c73fff4dc5072caae1715837175e4f6225c22e6a5a80ef437ce580927e1567a0", "cache_size": 1, "cache_capacity": 10000}
2026/01/23 21:51:36.622	DEBUG	events	event	{"name": "cached_managed_cert", "id": "1e2d8d24-afe4-4ee7-b41c-dc3c807f77f6", "origin": "tls", "data": {"sans":["107.220.81.150"]}}
2026/01/23 21:51:36.625	INFO	pki.ca.local	root certificate is already trusted by system	{"path": "storage:pki/authorities/local/root.crt"}
2026/01/23 21:51:36.625	DEBUG	events	event	{"name": "started", "id": "937bcf13-88a9-4629-a573-bfb6f12dc45c", "origin": "", "data": null}
2026/01/23 21:51:36.644	INFO	autosaved config (load with --resume flag)	{"file": "/Users/gribo/Library/Application Support/Caddy/autosave.json"}
2026/01/23 21:51:36.644	INFO	serving initial configuration
2026/01/23 21:51:36.644	INFO	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/Users/gribo/Library/Application Support/Caddy", "instance": "94566520-5ad5-4777-894f-46fb38c06b13", "try_again": "2026/01/24 21:51:36.644", "try_again_in": 86399.999999567}
2026/01/23 21:51:36.644	INFO	tls	finished cleaning storage units
Successfully started Caddy (pid=9237) - Caddy is running in the background

Same Error code: SSL_ERROR_INTERNAL_ERROR_ALERT in browser