Skip to content
ecdsa.com
Go crypto/tlsGo

Error message

tls: first record does not look like a TLS handshake

gRPC, database drivers and other libraries wrap it in their own messages, so search logs for the core text. For HTTP replies net/http says "http: server gave HTTP response to HTTPS client" instead.

Go crypto/tls — the peer's first bytes are not a TLS record (tls.Dial, gRPC, database drivers, http.Client)

What it means

Go's TLS client sent a ClientHello and the first record it got back does not have the structure of a TLS handshake record, so it gives up immediately. The peer speaks something else on that port: plain HTTP (when Go cannot recognise it as such), a plaintext gRPC server, a database or mail protocol greeting. Go servers log the reverse situation, a client speaking plain text to a TLS port, differently.

Why it happens

How to fix it

  1. 1.

    Check what the port speaks

    A TLS handshake with openssl, or a quick look at the banner, shows whether the port is TLS at all.

    bash
    # Does the port speak TLS?
    openssl s_client -connect api.example.com:8080 -servername api.example.com -brief </dev/null
    timeout 3 nc api.example.com 8080 </dev/null | head -1     # a text banner = plain protocol
  2. 2.

    gRPC: use the credentials the server expects

    TLS credentials for TLS servers; insecure credentials only for plaintext servers on trusted networks.

    go
    // gRPC: match the server. Plaintext server -> insecure credentials (only on trusted networks);
    // TLS server -> TLS credentials.
    conn, err := grpc.NewClient("api.example.com:9090",
    	grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{ServerName: "api.example.com"})))
  3. 3.

    Use STARTTLS where the protocol requires it

    net/smtp's StartTLS (and the equivalents in database drivers, e.g. sslmode for PostgreSQL) upgrade the connection in-band.

    go
    // SMTP on 587 starts in plain text: use STARTTLS, not tls.Dial
    c, err := smtp.Dial("mail.example.com:587")
    if err == nil {
    	err = c.StartTLS(&tls.Config{ServerName: "mail.example.com"})
    }

Related errors

Browse the full signature error database