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

OpenTelemetry Error Guide: 'server gave HTTP response to HTTPS client' — Fix OTLP TLS Mismatch

Quick answer

Fix 'http: server gave HTTP response to HTTPS client' on OTLP: the exporter uses TLS but the Collector is plaintext. Fix the scheme or tls.insecure.

  • #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 an OTLP client speaks TLS (an https:// endpoint or a gRPC exporter with TLS enabled) but the Collector is listening in plaintext. The client sends a TLS ClientHello, the server answers with a plain HTTP response, and the client rejects it. It surfaces in SDK stderr and in the Collector’s own exporter logs:

traces export: Post "https://otel-collector.example.com:4318/v1/traces": http: server gave HTTP response to HTTPS client

The gRPC exporter variant wraps the same cause in a transport error:

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: http: server gave HTTP response to HTTPS client\""}

http: server gave HTTP response to HTTPS client means the client expected TLS but the endpoint answered in cleartext — a scheme/port/tls.insecure mismatch, not a certificate problem.

Symptoms

  • Every export fails the same way; the error is deterministic, never intermittent.
  • The endpoint URL starts with https:// or the gRPC exporter has TLS enabled, yet the target port is plaintext.
  • Switching the endpoint to http:// (or setting tls.insecure: true) makes the error vanish.
  • curl https://endpoint/v1/traces fails while curl http://endpoint/v1/traces succeeds.
  • The Collector’s OTLP receiver has no tls: block, so it serves plaintext.
  • The mistake often follows copy-pasting a vendor’s https:// example onto a local plaintext Collector.

Common Root Causes

  • Endpoint scheme is https but the Collector is plaintext — the receiver has no tls: block, so it never speaks TLS on that port.
  • gRPC exporter TLS left enabledtls.insecure is unset (defaults to secure) while pointing at a plaintext gRPC receiver.
  • Wrong port — the client targets the plaintext port (4317/4318) but assumes it is TLS-terminated.
  • TLS terminated elsewhere — a load balancer or sidecar is supposed to terminate TLS but is bypassed, so the client hits the raw plaintext Collector.
  • Vendor sample copied verbatim — an https:// SaaS endpoint config was reused against a local test Collector that only serves cleartext.
  • Mixed HTTP/gRPC assumptions — the exporter protocol or port does not match how the receiver is actually configured.

Diagnostic Workflow

Confirm the exact endpoint, protocol, and whether the client is in secure mode:

echo "$OTEL_EXPORTER_OTLP_ENDPOINT"    # https://... means the client expects TLS
echo "$OTEL_EXPORTER_OTLP_PROTOCOL"    # grpc | http/protobuf
echo "$OTEL_EXPORTER_OTLP_INSECURE"    # true = plaintext for gRPC SDK exporters

Probe the endpoint both ways. If the plaintext request works and the TLS one fails, the server is plaintext:

# Plaintext succeeds -> the endpoint is NOT serving TLS
curl -v -m 5 http://otel-collector.example.com:4318/v1/traces -X POST -H 'Content-Type: application/json' -d '{}'
# TLS fails with the handshake/HTTP-response error
curl -v -m 5 https://otel-collector.example.com:4318/v1/traces -X POST -H 'Content-Type: application/json' -d '{}'

You then have two correct fixes. Option A — the endpoint really is plaintext (local/dev): make the client match it. For the Collector’s otlp exporter, set tls.insecure and use an http:// endpoint:

exporters:
  otlp:
    endpoint: otel-collector.example.com:4317
    tls:
      insecure: true          # plaintext gRPC; no TLS attempted
  otlphttp:
    endpoint: http://otel-collector.example.com:4318   # http scheme, not https

Option B — the endpoint should be TLS (production): enable TLS on the receiver instead of downgrading the client:

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
        tls:
          cert_file: /etc/otelcol/certs/server.crt
          key_file: /etc/otelcol/certs/server.key
      http:
        endpoint: 0.0.0.0:4318
        tls:
          cert_file: /etc/otelcol/certs/server.crt
          key_file: /etc/otelcol/certs/server.key

Validate and watch for the error clearing:

otelcol-contrib validate --config /etc/otelcol/config.yaml
journalctl -u otelcol-contrib --since '10 min ago' | grep -i 'https client\|tls\|export'

Example Root Cause Analysis

A developer copied a SaaS onboarding snippet that set OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.example.com:4318 and pointed it at a local Collector started from the default plaintext config. Every export failed with http: server gave HTTP response to HTTPS client, because the local receiver had no tls: block and answered the TLS ClientHello with a plain HTTP 200-style response.

The fix depended on intent. For the local dev loop, the endpoint was changed to http://otel-collector.example.com:4318 so the client stopped attempting TLS, and traces flowed immediately. For the staging gateway, the opposite fix was correct: a tls: block with cert_file/key_file was added to both the gRPC and HTTP receiver protocols so the endpoint genuinely served TLS, keeping the https:// scheme intact and encrypting telemetry on the wire.

Prevention Best Practices

  • Keep the endpoint scheme and the receiver configuration in lockstep: https:// (or gRPC secure) requires a tls: block on the receiver.
  • For local/dev Collectors, set tls.insecure: true explicitly so intent is obvious and no accidental TLS is attempted.
  • Never copy a vendor’s https:// endpoint onto a plaintext Collector without also configuring TLS or switching the scheme.
  • Standardize ports: reserve TLS listeners on dedicated ports so 4317/4318 are unambiguous across environments.
  • Terminate TLS at a well-known layer (Collector, sidecar, or LB) and document which one, so clients know whether to use TLS.
  • Add a smoke test that curls the real endpoint with the intended scheme in CI before shipping exporter config.

Quick Command Reference

# Show what the client expects
env | grep OTEL_EXPORTER_OTLP

# Prove the endpoint is plaintext (http works, https fails)
curl -v -m 5 http://otel-collector.example.com:4318/v1/traces -X POST -d '{}'
curl -v -m 5 https://otel-collector.example.com:4318/v1/traces -X POST -d '{}'

# Validate config after fixing scheme or tls block
otelcol-contrib validate --config /etc/otelcol/config.yaml

# Watch the error clear
journalctl -u otelcol-contrib -f | grep -i 'https client\|export'

Conclusion

http: server gave HTTP response to HTTPS client is a plaintext-versus-TLS mismatch: the exporter attempts TLS but the Collector endpoint answers in cleartext. Decide what the endpoint should be. For local development, point the client at http:// or set tls.insecure: true. For production, keep https:// and add a tls: block with cert_file/key_file to the receiver so the endpoint actually terminates TLS. Aligning the scheme, port, and receiver configuration makes the error disappear and keeps telemetry encrypted where it matters.

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.