A client hits your site and gets a certificate warning. The certificate presented is a self-signed cert with CN “TRAEFIK DEFAULT CERT” instead of the certificate you configured. Everything else looks fine: Traefik is up, /ping returns 200, backends are healthy, routes work. The proxy is completely healthy and the only broken thing is which certificate it picked during the TLS handshake.
This failure is invisible to most Traefik monitoring. There is no error counter, no 5xx, no failed health check. The TLS handshake succeeds; it just succeeds with the wrong certificate. Unless you probe TLS externally, you find out from a user’s screenshot.
This article covers how Traefik selects which certificate to present, why it falls back to the default, and how to find the specific mismatch in your configuration.
What this means
Traefik terminates TLS at the entrypoint and chooses which certificate to present during the TLS handshake, before any HTTP routing happens. The selection input is the Server Name Indication (SNI) extension the client sends in the TLS ClientHello. Router rules like Host() are evaluated after the handshake completes, so they play no role in certificate selection. This is the most common misconception behind this incident.
The selection logic, in order:
- If the client sends an SNI server name and Traefik has a certificate whose CN or SANs match it, Traefik presents that certificate.
- If there is no SNI, or no configured certificate matches the SNI, Traefik falls back to the default certificate from the TLS store, if you configured one (via
defaultCertificateordefaultGeneratedCert). - If no default certificate is configured either, Traefik generates a self-signed certificate on the fly, with CN “TRAEFIK DEFAULT CERT”, and serves that.
So “serving the default certificate” is not an error state inside Traefik. It is the defined fallback for “I could not match this handshake to any certificate you gave me.”
flowchart TD
A[Client TLS ClientHello with SNI] --> B{SNI matches a configured cert CN or SAN?}
B -- yes --> C[Serve matched certificate]
B -- no --> D{Default certificate configured in TLS store?}
D -- yes --> E[Serve configured default certificate]
D -- no --> F[Serve generated self-signed cert - TRAEFIK DEFAULT CERT]
C --> G[Handshake completes - routing happens after]
E --> G
F --> GOne important consequence: the cert presented and the router that eventually handles the request are chosen independently. A request can get the correct certificate and still 404, or get the default certificate and then route perfectly once the client accepts it. Diagnose the certificate layer separately from the routing layer. See Traefik 404 not found for the routing side.
There is also one benign case: during first-boot ACME acquisition, Traefik serves the self-signed default certificate until issuance completes. If you see the default cert for the first minutes after a fresh deploy and then the real cert appears, that is expected behavior, not a fault.
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| No certificate matches the SNI | Clients for one specific hostname get the default cert; other hostnames on the same Traefik are fine | openssl s_client -servername for the failing hostname, then compare against certs Traefik has loaded |
| No default certificate configured | Every unmatched or SNI-less connection gets the self-signed “TRAEFIK DEFAULT CERT” | TLS store configuration: is defaultCertificate or defaultGeneratedCert set? |
| First-boot ACME acquisition in progress | Default cert served briefly after a fresh deploy, then replaced by the real cert | Traefik logs for ACME issuance; check again after a few minutes |
| Router without TLS enabled, or cert not associated with the route | HTTP works, HTTPS serves default cert for that route’s hostname | Router’s TLS configuration and which certificate resolver or store it references |
| TLS store misconfiguration | A configured defaultCertificate is silently ignored; generated cert still served | Whether the store definition is in the dynamic configuration and named default |
| Certificate loaded but for a different name | Cert exists in Traefik but its CN/SANs do not cover the requested hostname | Inspect the certificate’s SAN list against the failing hostname |
Quick checks
All of these are safe and read-only.
# See which certificate Traefik presents for a specific hostname.
# This is the single most diagnostic command for this issue.
echo | openssl s_client -servername app.example.com -connect traefik-host:443 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates -ext subjectAltName
If the subject shows CN = TRAEFIK DEFAULT CERT, Traefik had no matching certificate for app.example.com and no configured default. If the subject is a real cert but for a different domain than you expected, your default certificate is configured and the SNI matched nothing specific.
# Check what an SNI-less client gets (approximates scanners and very old clients).
echo | openssl s_client -connect traefik-host:443 2>/dev/null \
| openssl x509 -noout -subject -issuer
# List certificates Traefik has loaded, with expiry.
# Labels include cn and sans, so you can check coverage for the failing hostname.
curl -s http://localhost:8080/metrics | grep traefik_tls_certs_not_after
# Inspect loaded routers and confirm TLS is enabled on the route you expect.
curl -s http://localhost:8080/api/http/routers | jq '.[] | {name, rule, tls}'
# Check TLS versions and ciphers actually being negotiated, per entrypoint.
curl -s http://localhost:8080/metrics | grep traefik_entrypoint_requests_tls_total
# Search logs for ACME issuance activity if this is a fresh deployment.
# Exact wording varies by version.
grep -iE "acme|certificate" /var/log/traefik/traefik.log | tail -50
How to diagnose it
Work from the client inward. Each step narrows which layer dropped the match.
Reproduce from a controlled client. Run the
openssl s_clientcheck above against the failing hostname, both through your normal path and directly against Traefik. If the direct connection gets the right cert but the load balancer path gets the default, something in front (another TLS terminator, a CDN) is intercepting, and Traefik is not your problem.Record exactly what was served. Subject, issuer, SAN list, expiry. Three outcomes: (a) self-signed “TRAEFIK DEFAULT CERT”, meaning no configured default and no SNI match; (b) a real certificate for a different domain, meaning a configured default is being served because the SNI matched nothing specific; (c) the correct certificate, meaning the problem is client-side (trust store, old CA bundle) rather than selection.
Check what certificates Traefik actually has. Use
traefik_tls_certs_not_afterand compare thecn/sanslabels against the failing hostname. If no loaded certificate covers it, the selection behavior is correct and the problem is upstream: the certificate was never obtained or never loaded. If ACME manages that cert, this becomes a renewal or issuance failure. See Traefik certificate expired and Traefik ACME rate limit.Confirm the route references TLS correctly. Via
/api/http/routers, check that the router for the hostname has TLS enabled and references the resolver or store you expect. A route without TLS configuration will still terminate TLS at the entrypoint if the entrypoint itself does TLS, but with no per-route certificate association, selection falls to SNI matching against the store.Check the TLS store default. If you intended a specific certificate as the default (for SNI-less clients and unmatched names), verify
defaultCertificateordefaultGeneratedCertis defined in the dynamic configuration and that the store is the one Traefik actually uses. If both are defined,defaultCertificatewins. If the store is defined in the wrong place (for example, in static config or a file the provider is not watching), it is silently absent.Rule out the benign case. If the affected hostname was deployed minutes ago and ACME is still acquiring the certificate, wait for issuance and re-run step 1. Check logs for challenge errors if it does not resolve. See Traefik ACME rate limit if issuance keeps failing.
Check for version-specific selection bugs. Traefik v3.7 changed Gateway API behavior: if the Gateway API CRDs are not updated to the expected version, the provider’s informer never syncs, no listener is programmed, and every HTTPS connection falls back to the default self-signed cert while health checks stay green. If the symptom appeared immediately after a v3.7 upgrade on Kubernetes Gateway API, check logs for
Failed to watcherrors on TLSRoute or BackendTLSPolicy and verify CRD versions. There were also fixes for nondeterministic certificate selection when multiple loaded certificates share SANs (overlapping wildcard and specific certs). If you have overlapping certificates, upgrading may be the fix.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
traefik_tls_certs_not_after (labels: cn, sans, serial) | Inventory of loaded certificates and their expiry; confirms whether the cert for the failing hostname exists at all | Hostname absent from the label set, or a cert covering it approaching expiry |
traefik_entrypoint_requests_tls_total (labels: tls_version, tls_cipher) | Confirms TLS handshakes are completing, and on which entrypoint; a mismatch between handshake success and client-reported errors points at selection, not transport | Handshakes succeeding while clients report cert warnings |
traefik_entrypoint_requests_total{code="404"} | Rising entrypoint 404s alongside default-cert reports suggest routes are missing too, which points at provider desync rather than a cert problem | 404 rate rising on hostnames that previously worked |
traefik_config_last_reload_success | A frozen timestamp means stale configuration; a newly deployed cert or TLS store change may simply not be loaded | Timestamp not advancing while config changes are being made |
| External synthetic TLS probes per production hostname | The only signal that actually detects “wrong cert served”. Traefik has no metric for certificate selection outcome | Probe sees unexpected subject/issuer or a self-signed cert |
Nothing in Traefik’s Prometheus metrics tells you which certificate was presented for a handshake. The selection failure is only observable from the client side. Without external TLS probes, you are blind to this entire failure class.
Fixes
Add the missing certificate for the hostname
If the SNI simply has no matching cert, obtain or load one that covers the hostname. For ACME-managed domains, fix whatever is blocking issuance (challenge reachability, rate limits, corrupted acme.json). For manually managed certs, make sure the certificate and key are defined where your provider can load them, and that the SAN list covers every hostname you serve, including bare-domain and www variants.
Tradeoff: none. This is the correct fix when the inventory check showed the cert missing.
Configure a deliberate default certificate
If you serve many hostnames and want unmatched or SNI-less connections to get a real, valid certificate instead of the self-signed fallback, set defaultCertificate (or defaultGeneratedCert for a generated cert with controlled parameters) in the TLS store. The default certificate is what non-SNI clients and scanners will see.
Tradeoff: whatever you choose as the default is publicly visible for any unmatched name, so pick something you do not mind being associated with arbitrary traffic. It also masks misconfigurations: a hostname with a missing cert now serves the default instead of failing loudly, which delays detection of the missing cert.
Enable strict SNI if you want mismatches to fail closed
If your policy is “no valid cert, no handshake”, strict SNI checking makes Traefik refuse the handshake when no certificate matches, instead of serving the default. This converts silent wrong-cert serving into an immediate, loud connection failure, which is easier to alert on and does not leak a certificate for the wrong name.
Tradeoff: clients without SNI (rare, but they exist) and any hostname with a missing cert get a hard failure instead of a working-but-wrong-cert connection. During a cert outage, the blast radius is total for the affected name rather than degraded.
Fix the TLS store or route association
If a configured default is being ignored, verify the store definition lives in the dynamic configuration (file provider, or the appropriate CRD on Kubernetes), that it is the store Traefik actually consults, and that the route in question has TLS enabled with the expected reference. Remember the selection order: per-SNI match first, store default second, generated cert last.
Wait out first-boot ACME, then investigate if it persists
For the benign first-boot case, no action is needed if the real cert appears within a few minutes. If the default cert persists, treat it as an ACME issuance failure and debug the challenge path.
Prevention
- Probe TLS externally, per production hostname. This is the only reliable detector for wrong-cert serving. Check subject, issuer, expiry, and SAN coverage, not just “handshake succeeded”. A probe that only checks TCP 443 open will never catch this.
- Alert on
traefik_tls_certs_not_afterwell before expiry. A missing or expiring cert today is a default-cert incident next week. Traefik starts renewal attempts 30 days out for Let’s Encrypt, so treat renewal as broken long before expiry; ticket at under 7 days remaining. - Decide your default-cert policy explicitly. Either configure a deliberate default certificate, or enable strict SNI so mismatches fail closed. The worst option is the implicit one: the self-signed fallback served to real users while nobody watches.
- Baseline which hostnames should serve which certificates and alert when the served subject changes. Cert changes outside a deployment window are a signal worth paging on.
- After Traefik upgrades, verify certificate serving before declaring success. Version-specific selection bugs and provider sync failures (the v3.7 Gateway API case above) present as default-cert serving with green health checks. A one-line
openssl s_clientcheck per hostname in your post-upgrade checklist catches this class entirely.
How Netdata helps
Netdata surfaces the signals that bracket this failure, even though the selection decision itself is only client-observable:
- Certificate inventory and expiry via
traefik_tls_certs_not_after, with per-certificate CN and SAN labels, so you can confirm whether a cert for the failing hostname is loaded and how long it has left. - TLS negotiation breakdown via
traefik_entrypoint_requests_tls_total, showing handshake volume by TLS version and cipher per entrypoint, which confirms handshakes are completing while clients complain. - Config freshness via
traefik_config_last_reload_success, so you can tell immediately whether a newly added certificate or TLS store change was ever loaded, or whether Traefik is running stale config. - Entrypoint 404 rate to distinguish a pure certificate problem from a wider routing or provider desync problem, since the two often travel together after a bad deploy or provider outage.
- Correlation across these signals in one view: default-cert reports plus a frozen config timestamp plus missing cert inventory points at provider desync; default-cert reports plus a present-but-expiring cert points at ACME renewal failure. Seeing them together is what turns a user screenshot into a root cause in minutes.
Related guides
- Traefik 404 not found: requests arriving with no matching router
- Traefik 502 Bad Gateway: when the backend is unreachable or returns garbage
- Traefik 503 Service Unavailable: no healthy backends left in the pool
- Traefik 504 Gateway Timeout: the backend is alive but too slow
- Traefik 5xx error rate: telling Traefik-generated errors from backend errors
- Traefik ACME rate limit: too many certificates already issued for this domain
- Traefik backend connection pool: keep-alive, MaxIdleConnsPerHost, and reuse
- Traefik cannot assign requested address: ephemeral port exhaustion
- Traefik cascading backend failure: how a partial outage becomes a total one
- Traefik certificate expired: when ACME renewal has been failing silently
- Traefik circuit breaker: shedding load from a failing backend
- Traefik CLOSE_WAIT pile-up: backend connections that never close






