No certificates found

1. The problem I’m having:

When I make a https request to a subdomain I get a tls error. When I review the logs I get “no certificates found.” Other than journalct -xfvu caddy not sure where else to look for information. Is there a location where the certs would be stored to see if they are there?

2. Error messages and/or full log output:

Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"info","ts":1739401976.2252278,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0xc0008e4100"}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"info","ts":1739401976.2273462,"msg":"serving initial configuration"}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"info","ts":1739401976.2284348,"logger":"tls","msg":"storage cleaning happened too recently; skipping for now","storage":"FileStorage:/var/lib/caddy/.local/share/caddy","instance":"1b0e2d2e-a23a-4833-a78f-f1d033ee8e5b","try_again":1739488376.2284322,"try_again_in":86399.99999903}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"info","ts":1739401976.2293382,"logger":"tls","msg":"finished cleaning storage units"}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"debug","ts":1739401976.4420645,"logger":"events","msg":"event","name":"tls_get_certificate","id":"d09baa61-9521-4444-858b-b15af949957e","origin":"tls","data":{"client_hello":{"CipherSuites":[64250,4865,4866,4867,49195,49199,49196,49200,52393,52392,49171,49172,156,157,47,53],"ServerName":"www.foundryserver.ca","SupportedCurves":[60138,4588,29,23,24],"SupportedPoints":"AA==","SignatureSchemes":[1027,2052,1025,1283,2053,1281,2054,1537],"SupportedProtos":["h2","http/1.1"],"SupportedVersions":[6682,772,771],"RemoteAddr":{"IP":"70.66.209.81","Port":65204,"Zone":""},"LocalAddr":{"IP":"192.168.255.242","Port":8443,"Zone":""}}}}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"debug","ts":1739401976.4429224,"logger":"tls.handshake","msg":"no matching certificates and no custom selection logic","identifier":"www.foundryserver.ca"}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"debug","ts":1739401976.4431572,"logger":"tls.handshake","msg":"choosing certificate","identifier":"*.foundryserver.ca","num_choices":1}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"debug","ts":1739401976.4433582,"logger":"tls.handshake","msg":"default certificate selection results","identifier":"*.foundryserver.ca","subjects":["*.foundryserver.ca"],"managed":true,"issuer_key":"acme-v02.api.letsencrypt.org-directory","hash":"3d483229b90ac5d6a40751d831e3b5c6ccc629d8474a57c53ab1e6aa24b90822"}
Feb 12 15:12:56 caddy1 caddy[49126]: {"level":"debug","ts":1739401976.4435668,"logger":"tls.handshake","msg":"matched certificate in cache","remote_ip":"70.66.209.81","remote_port":"65204","subjects":["*.foundryserver.ca"],"managed":true,"expiration":1747172068,"hash":"3d483229b90ac5d6a40751d831e3b5c6ccc629d8474a57c53ab1e6aa24b90822"}
F

3. Caddy version:

v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY=

4. How I installed and ran Caddy:

I used apt via the caddy documentation for debian

a. System environment:

Linux caddy1 6.1.0-31-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.128-1 (2025-02-07) x86_64 GNU/Linux
Systemd

b. Command:

systemctl start caddy

c. Service/unit/compose file:

Default systemd file, no changes.

d. My complete Caddy config:

{
	debug
	http_port 8080
	https_port 8443
	metrics

	admin 0.0.0.0:4334

	email admin@foundryserver.com
}

# Domain specific server blocks
*.foundryserver.ca {
	tls {
		dns cloudflare {redacted}
	}

       # customer routes
	map {host}:8080 {backend_ip} {
		joe.foundryserver.ca "192.168.255.200"
	}

	reverse_proxy {host}:8080 {backend_ip}:30000 {
		# values for websocket connection
		stream_close_delay 8h
	}

        # all other routes that don't match the customer routes.
	reverse_proxy *.foundryserver.ca:8080 172.168.10.10:80

	handle_errors 5xx {
		# This is used to catch a request for a customer game server that is not running.
		# This will NOT catch an unknown user subdomain.
		root * /var/www/error_pages/5xx_error.html
		file_server
	}
}

Output of list modules.


```root@caddy1:/var/log/caddy# caddy list-modules
admin.api.load
admin.api.metrics
admin.api.pki
admin.api.reverse_proxy
caddy.adapters.caddyfile
caddy.config_loaders.http
caddy.filesystems
caddy.listeners.http_redirect
caddy.listeners.proxy_protocol
caddy.listeners.tls
caddy.logging.cores.mock
caddy.logging.encoders.append
caddy.logging.encoders.console
caddy.logging.encoders.filter
caddy.logging.encoders.filter.cookie
caddy.logging.encoders.filter.delete
caddy.logging.encoders.filter.hash
caddy.logging.encoders.filter.ip_mask
caddy.logging.encoders.filter.query
caddy.logging.encoders.filter.regexp
caddy.logging.encoders.filter.rename
caddy.logging.encoders.filter.replace
caddy.logging.encoders.json
caddy.logging.writers.discard
caddy.logging.writers.file
caddy.logging.writers.net
caddy.logging.writers.stderr
caddy.logging.writers.stdout
caddy.storage.file_system
events
http
http.authentication.hashes.bcrypt
http.authentication.providers.http_basic
http.encoders.gzip
http.encoders.zstd
http.handlers.acme_server
http.handlers.authentication
http.handlers.copy_response
http.handlers.copy_response_headers
http.handlers.encode
http.handlers.error
http.handlers.file_server
http.handlers.headers
http.handlers.intercept
http.handlers.invoke
http.handlers.log_append
http.handlers.map
http.handlers.metrics
http.handlers.push
http.handlers.request_body
http.handlers.reverse_proxy
http.handlers.rewrite
http.handlers.static_response
http.handlers.subroute
http.handlers.templates
http.handlers.tracing
http.handlers.vars
http.ip_sources.static
http.matchers.client_ip
http.matchers.expression
http.matchers.file
http.matchers.header
http.matchers.header_regexp
http.matchers.host
http.matchers.method
http.matchers.not
http.matchers.path
http.matchers.path_regexp
http.matchers.protocol
http.matchers.query
http.matchers.remote_ip
http.matchers.tls
http.matchers.vars
http.matchers.vars_regexp
http.precompressed.br
http.precompressed.gzip
http.precompressed.zstd
http.reverse_proxy.selection_policies.client_ip_hash
http.reverse_proxy.selection_policies.cookie
http.reverse_proxy.selection_policies.first
http.reverse_proxy.selection_policies.header
http.reverse_proxy.selection_policies.ip_hash
http.reverse_proxy.selection_policies.least_conn
http.reverse_proxy.selection_policies.query
http.reverse_proxy.selection_policies.random
http.reverse_proxy.selection_policies.random_choose
http.reverse_proxy.selection_policies.round_robin
http.reverse_proxy.selection_policies.uri_hash
http.reverse_proxy.selection_policies.weighted_round_robin
http.reverse_proxy.transport.fastcgi
http.reverse_proxy.transport.http
http.reverse_proxy.upstreams.a
http.reverse_proxy.upstreams.multi
http.reverse_proxy.upstreams.srv
pki
tls
tls.ca_pool.source.file
tls.ca_pool.source.http
tls.ca_pool.source.inline
tls.ca_pool.source.pki_intermediate
tls.ca_pool.source.pki_root
tls.ca_pool.source.storage
tls.certificates.automate
tls.certificates.load_files
tls.certificates.load_folders
tls.certificates.load_pem
tls.certificates.load_storage
tls.client_auth.verifier.leaf
tls.get_certificate.http
tls.get_certificate.tailscale
tls.handshake_match.local_ip
tls.handshake_match.remote_ip
tls.handshake_match.sni
tls.handshake_match.sni_regexp
tls.issuance.acme
tls.issuance.internal
tls.issuance.zerossl
tls.leaf_cert_loader.file
tls.leaf_cert_loader.folder
tls.leaf_cert_loader.pem
tls.leaf_cert_loader.storage
tls.permission.http
tls.stek.distributed
tls.stek.standard

  Standard modules: 124

dns.providers.cloudflare

  Non-standard modules: 1

  Unknown modules: 0

The log line is just saying it couldn’t find an exact match, so it searched for a match based on the wildcard. You can see it’s picking up the right certificate in the subsequent log lines.

Your site has a redirect loop, which is the cause of the error you see on the browser. Here’s curl output:

~ $ curl -v https://www.foundryserver.ca/
* Host www.foundryserver.ca:443 was resolved.
* IPv6: (none)
* IPv4: 38.186.49.166
*   Trying 38.186.49.166:443...
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* 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_AES_128_GCM_SHA256 / x25519 / id-ecPublicKey
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=*.foundryserver.ca
*  start date: Feb 12 21:34:28 2025 GMT
*  expire date: May 13 21:34:27 2025 GMT
*  subjectAltName: host "www.foundryserver.ca" matched cert's "*.foundryserver.ca"
*  issuer: C=US; O=Let's Encrypt; CN=E5
*  SSL certificate verify ok.
*   Certificate level 0: Public key type EC/prime256v1 (256/128 Bits/secBits), signed using ecdsa-with-SHA384
*   Certificate level 1: Public key type EC/secp384r1 (384/192 Bits/secBits), signed using sha256WithRSAEncryption
*   Certificate level 2: Public key type RSA (4096/152 Bits/secBits), signed using sha256WithRSAEncryption
* Connected to www.foundryserver.ca (38.186.49.166) port 443
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://www.foundryserver.ca/
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: www.foundryserver.ca]
* [HTTP/2] [1] [:path: /]
* [HTTP/2] [1] [user-agent: curl/8.12.0]
* [HTTP/2] [1] [accept: */*]
> GET / HTTP/2
> Host: www.foundryserver.ca
> User-Agent: curl/8.12.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* Request completely sent off
< HTTP/2 308
< alt-svc: h3=":8443"; ma=2592000
< content-type: text/html; charset=utf-8
< location: /
< server: Caddy
< content-length: 37
< date: Wed, 12 Feb 2025 23:37:59 GMT
<
<a href="/">Permanent Redirect</a>.

* Connection #0 to host www.foundryserver.ca left intact

You can see the Location header pointing at the same location being requested, i.e. /.

Given www.foundryserver.ca isn’t one of the source input in the map directive, the placeholder {backend_ip} will not be set. This means the following reverse_proxy config is asking Caddy to proxy the requests to www.foundryserver.ca:8080 and :30000 in random load-balancing policy.

There’s then this line which asks Caddy to proxy the requests to the upstream *.foundryserver.ca:8080.

Are you trying to use those first arguments as matchers? If yes, the only matcher you can use in-line like that is the path matcher. All other matchers have to be named (see also: Caddyfile Concepts — Caddy Documentation).

Ok your observations were totally correct. There is a subtlety to the flow if the incoming requests that I didn’t appreciate. You have to terminate or resolve the incoming request within the server block, or it will keep going. Here is the working config for anyone that might need it. The goal is to route company websites to one back end. The rest of the subdomains are customers websites and they go to a different backend.

I do have another question, I will using the rest api to add/edit/delete map entries. can I use the @id for each entry so that I can just interact with that entry instead of rebuilding the whole json string to send to the rest api?

{
	debug
	http_port 8080
	https_port 8443
	metrics

	admin 0.0.0.0:4334

	email admin@foundryserver.com
}

# Domain specific server blocks

foundryserver.ca, www.foundryserver.ca, stripe.foundryserver.ca, max.foundryserver.ca {
	# Get the SSL certificate from Cloudflare for each subdomain, cant use wild card here.
	tls {
		dns cloudflare {redacted}
	}

	# Redirect all traffic to the kubernetes ingress service which will have its own routing rules.
	reverse_proxy 172.16.10.10

	# This will handle any 5xx errors that are generated by the backend services.
	handle_errors 5xx {
		file_server {
			index /5xx_error.html
			root /var/www/error_pages/foundryserver
		}
	}
}

*.foundryserver.ca {
	# Get the SSL certificate from Cloudflare for wildcard subdomains.
	tls {
		dns cloudflare {redacted}
	}

	# This map will relate a host name to a backend ip address.
	map {host} {backend_ip} {
		example.foundryserver.ca "192.168.255.200"
	}

	# This will take the mapping and router the traffic to the correct backend service.
	reverse_proxy {host}:8080 {backend_ip}:30000 {
		# No matter what a web socket connection will close after 8 hours.
		stream_close_delay 8h
	}

	# This is used to catch a request for a customer game server that is not running.
	handle_errors 5xx {
		file_server {
			index /5xx_error.html
			root /var/www/error_pages/game_servers
		}
	}
}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.