Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for OpenTelemetry By James Joyner IV · · 9 min read

OpenTelemetry Error Guide: 'x509: certificate is valid for ... not ...' — Fix OTLP TLS Hostname Mismatch

Quick answer

Fix 'x509: certificate is valid for collector.internal, not otel.example.com': SAN mismatch. Use the right DNS name or server_name_override.

  • #opentelemetry
  • #observability
  • #troubleshooting
  • #errors
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

This error appears when the TLS handshake succeeds cryptographically but the server certificate’s Subject Alternative Names (SANs) do not include the hostname the exporter connected to. The client trusts the CA, yet refuses the identity. It shows up in SDK stderr and in the Collector’s otlp exporter logs:

error	exporterhelper/queue_sender.go:128	Exporting failed. Dropping data.	{"kind": "exporter", "data_type": "traces", "name": "otlp", "error": "rpc error: code = Unavailable desc = connection error: desc = \"transport: authentication handshake failed: x509: certificate is valid for collector.internal, not otel.example.com\""}

The HTTP/OTLP variant reads:

traces export: Post "https://otel.example.com:4318/v1/traces": tls: failed to verify certificate: x509: certificate is valid for collector.internal, not otel.example.com

x509: certificate is valid for collector.internal, not otel.example.com means the endpoint host (otel.example.com) is not listed in the certificate’s SANs (which only cover collector.internal) — an identity mismatch, not an untrusted-CA or expiry problem.

Symptoms

  • Every export fails with a hostname mismatch; the CA itself is trusted, so it is not an “unknown authority” error.
  • The error names two hosts: what the cert is valid for, and what you connected to.
  • Connecting by the cert’s real SAN (e.g. collector.internal) works, while the alias or IP fails.
  • The exporter endpoint uses an IP address or a DNS alias not present in the cert SANs.
  • openssl s_client ... -verify_hostname confirms the mismatch.
  • Setting server_name_override to the cert’s SAN clears the error.

Common Root Causes

  • Endpoint host not in the cert SANs — the exporter targets otel.example.com but the cert only lists collector.internal.
  • Connecting by IP — the endpoint is 10.0.7.9:4317 and the cert has no IP SAN for that address.
  • DNS alias / CNAME — a friendly alias points at the Collector, but the cert was issued for the backend’s real name.
  • Load balancer or shared cert — the LB presents a cert for its own hostname, not the OTLP service name clients use.
  • Legacy CN-only certificate — the cert relies on a Common Name with no SANs, which modern Go TLS clients reject outright.
  • Cert reused across environments — a collector.internal cert copied to a service that clients reach as otel.example.com.

Diagnostic Workflow

First inspect the certificate the endpoint actually presents and list its SANs:

# Print the SANs the server cert covers
openssl s_client -connect otel.example.com:4317 -servername otel.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 'Subject Alternative Name'

Compare that list to the host in your exporter endpoint:

echo "$OTEL_EXPORTER_OTLP_ENDPOINT"    # the host here must appear in the SANs above
env | grep OTEL_EXPORTER_OTLP

The correct fix is almost always to connect by a name the certificate is valid for. Point the exporter at the cert’s real SAN:

exporters:
  otlp:
    endpoint: collector.internal:4317   # a name present in the cert SANs
    tls:
      insecure: false
      ca_file: /etc/otelcol/certs/server-ca.crt

When you must keep an alias or IP endpoint (for example, connecting via an LB VIP), use server_name_override so verification checks the cert against the SAN name instead of the dialed host:

exporters:
  otlp:
    endpoint: 10.0.7.9:4317                 # IP or alias with no matching SAN
    tls:
      insecure: false
      ca_file: /etc/otelcol/certs/server-ca.crt
      server_name_override: collector.internal   # verify against this SAN

Reserve insecure_skip_verify for short-lived debugging only — it disables identity checks entirely and must never be shipped. The clean fix is either the right name or server_name_override. Validate and confirm:

otelcol-contrib validate --config /etc/otelcol/config.yaml
journalctl -u otelcol-contrib --since '10 min ago' | grep -i 'certificate is valid for\|x509\|handshake'

Example Root Cause Analysis

A platform team fronted their gateway Collector with an internal load balancer and told application teams to export to otel.example.com:4317. The server certificate, however, had been issued for the backend hostname collector.internal and carried no SAN for the alias. Every exporter that used the friendly name failed with x509: certificate is valid for collector.internal, not otel.example.com, and traces silently stopped.

Two fixes were viable, and the team applied the durable one. Short term, exporters that could not be re-issued quickly set server_name_override: collector.internal so TLS verification matched the cert’s SAN while still dialing the alias — no security downgrade, since the CA and identity were still checked. Long term, the certificate was reissued with both collector.internal and otel.example.com (plus the LB VIP as an IP SAN) in the SAN list, after which the override was removed and every client verified cleanly against the alias.

Prevention Best Practices

  • Issue Collector certificates with SANs covering every name and IP clients actually use, including LB aliases and VIPs.
  • Prefer connecting by a real DNS SAN over an IP address; add IP SANs only when IP endpoints are unavoidable.
  • Use server_name_override as a deliberate, documented bridge — never insecure_skip_verify in production.
  • Keep certificates environment-specific; do not reuse a collector.internal cert for a service clients reach by another name.
  • Automate cert issuance (cert-manager or your CA tooling) so SAN lists stay in sync with endpoints as they change.
  • Add a startup check that runs openssl s_client -verify_hostname against the intended endpoint before rollout.

Quick Command Reference

# List the SANs the server cert actually covers
openssl s_client -connect otel.example.com:4317 -servername otel.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -text | grep -A1 'Subject Alternative Name'

# Verify the hostname explicitly
openssl s_client -connect otel.example.com:4317 -verify_hostname otel.example.com </dev/null

# Confirm the endpoint host the exporter uses
env | grep OTEL_EXPORTER_OTLP

# Validate config after setting the right name or server_name_override
otelcol-contrib validate --config /etc/otelcol/config.yaml

Conclusion

x509: certificate is valid for collector.internal, not otel.example.com is an identity mismatch: the CA is trusted but the endpoint host is missing from the certificate’s SANs. The right fix is to connect by a name the cert covers, or, when an alias or IP endpoint is unavoidable, to set server_name_override to the SAN so verification stays intact. Avoid insecure_skip_verify outside of momentary debugging. Best of all, reissue the certificate with SANs for every name and IP clients use, and the mismatch never recurs.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.