Practical guide · Traefik
Traefik serves the wrong certificate: check SNI, routers and ACME
Your hostname points to Traefik, but the browser receives a default certificate or one for another name. Start with a fresh TLS connection. The certificate is selected before HTTP routing, so a correct Host rule alone does not prove that the expected certificate is available.
This guide covers Traefik v3 terminating HTTPS, with examples for Docker Compose and the file provider. Replace example.com, 192.0.2.10, paths and service names with your own. Shell commands assume Linux, Bash, OpenSSL and GNU timeout. Check only endpoints you operate or are authorized to inspect. The local demonstration was tested with Traefik 3.7.16.
1. Inspect the certificate with the correct SNI
Run the probe from outside the hosting network. Keep the hostname in -servername even when -connect uses an IP address. Record the SHA-256 fingerprint, issuer, validity dates and Subject Alternative Name entries. This command displays a certificate; it does not validate trust in the chain or hostname matching.
set -o pipefail
timeout 10 openssl s_client -connect example.com:443 \
-servername example.com </dev/null |
openssl x509 -noout -sha256 -fingerprint \
-issuer -dates -ext subjectAltName A default certificate can indicate that no loaded certificate matches the SNI, or that the client omitted SNI. With strict SNI checking, Traefik can reject that connection instead. If no certificate is printed, investigate the connection error. Adding an HTTP Host header cannot change a certificate already selected during the TLS handshake.
2. Check the HTTPS router and its certificate resolver
For a Docker application using Traefik-managed ACME, inspect the labels on the running application container. The router must use the intended entry point and have TLS enabled. The certresolver value must name an existing resolver, unless it is inherited from the entry point. These labels are an excerpt to adapt to an existing application; they do not configure its backend port or the Docker provider.
labels:
- "traefik.http.routers.app.rule=Host(`example.com`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls=true"
- "traefik.http.routers.app.tls.certresolver=letsencrypt" The resolver itself belongs in Traefik’s static configuration. The following HTTP challenge example also declares its entry points. Adapt the contact address and persistent storage path. Public port 80 must reach the web entry point for this challenge. For DNS validation or a different challenge, retain the matching configuration instead.
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
certificatesResolvers:
letsencrypt:
acme:
email: [email protected]
storage: /data/acme.json
httpChallenge:
entryPoint: web Declaring a resolver does not automatically attach it to every router. Static configuration changes require a Traefik restart; configuration delivered by a provider can update dynamically. Check the active configuration and errors before restarting. An HTTP Host rule can supply names for ACME issuance, while SNI selects the certificate presented on each TLS connection.
3. Separate an ACME failure from a selection problem
If Traefik obtains the certificates itself, inspect its recent logs. In this Compose example the service is named traefik. Look for an unknown resolver, a failed challenge, a staging issuer or a storage permission error. Follow the reported error rather than repeatedly requesting a new certificate.
docker compose logs --since=30m traefik The configured acme.json contains private keys and account material. Keep it private, persistent across container replacement and accessible to Traefik with the required file permissions (600). Do not delete it as a troubleshooting shortcut or share it between concurrent Traefik instances. A successful issuance log is evidence of issuance, not a check of what every public address serves.
4. If certificates come from files, check the loaded paths
Use this branch when another tool issues the certificates. Inspect tls.certificates in the dynamic file configuration and check that certFile and keyFile resolve inside the Traefik container. The certificate must cover the requested hostname and match its private key. The example below is a dynamic configuration excerpt, not a complete Traefik installation.
tls:
certificates:
- certFile: /certs/example.com/fullchain.pem
keyFile: /certs/example.com/privkey.pem Enable the file provider with watch: true in the static configuration. For reliable file notifications with bind mounts, mount the parent directory. The demonstration changes the dynamic configuration to reference a new certificate/key pair, then replaces the configuration file atomically. After a renewal, verify that Traefik actually loaded the update; replacing a file on disk alone is not evidence that the served certificate changed.
5. Reproduce a default-certificate response locally
The downloadable script runs Traefik on 127.0.0.1 with an automatically assigned port. It creates self-signed certificates for fallback.test, unrelated.test and renewal.test. Initially only the unrelated certificate and fallback are configured. The renewal.test probe gets the fallback. The script then loads the matching certificate through the file provider and checks the new connection without restarting Traefik.
Download and inspect the Traefik demonstration
Requirements: Linux, Bash, OpenSSL, GNU timeout and a local Docker daemon. Docker may download the pinned image. Read the script, then run bash traefik-certificate-selection.sh. BEFORE shows the fallback fingerprint; CONTROL shows the unrelated certificate selected by its SNI; AFTER shows the matching certificate. NO_SNI still shows the fallback. Assertions check the fingerprints and unchanged container start time. Temporary keys and the container are removed on exit. This tests certificate selection and dynamic loading, not ACME issuance or renewal.
6. Check the public paths, then keep monitoring
Probe each relevant public address while preserving the hostname as SNI. Replace the example address below; for IPv6 use [address]:443. A CDN can terminate TLS before Traefik and present its own certificate. With TCP TLS passthrough, the backend presents the certificate instead. Check the component that terminates each connection. Different replicas, IPv4/IPv6 paths or RSA/ECDSA handshakes can produce different observations.
set -o pipefail
timeout 10 openssl s_client -connect 192.0.2.10:443 \
-servername example.com </dev/null |
openssl x509 -noout -sha256 -fingerprint \
-issuer -dates -ext subjectAltName Nocert periodically observes certificates served by reachable TLS endpoints and evaluates expiration alert rules in a daily cycle. Check the observed endpoint and observation time, then configure the alert recipients. This complements the immediate external probe. Nocert does not configure your Traefik resolver, renew certificates or guarantee that every visitor receives the same certificate.