Skip to content
ecdsa.com
Go net/httpGo

Error message

http: server gave HTTP response to HTTPS client

Programs add their own prefix: Docker prints "Error response from daemon: Get \"https://<registry>/v2/\": http: server gave HTTP response to HTTPS client", Go programs "Get \"https://…\": http: server gave …". When the reply is not HTTP either, Go says "tls: first record does not look like a TLS handshake".

Go net/http — the TLS handshake received a plain HTTP response (also printed by Docker, Helm, Terraform)

What it means

Go's HTTP client started a TLS handshake, and the server answered with a plain-text HTTP response; Go recognises the "HTTP/" bytes and reports that the server speaks HTTP, not HTTPS. The most famous case is docker pull or docker login against a self-hosted registry running without TLS: Docker requires HTTPS for every registry that is not on a loopback address or explicitly marked insecure.

Why it happens

How to fix it

  1. 1.

    Confirm the port speaks plain HTTP

    An HTTP status line from http:// confirms it. Use the TLS endpoint (or http:// where that is acceptable).

    bash
    curl -si http://registry.example.com:5000/v2/ | head -1   # answers over plain HTTP?
  2. 2.

    Serve the registry over TLS

    Give the registry a certificate (public ACME or company CA) and configure it, or put a TLS-terminating proxy in front.

    yaml
    # Docker registry (distribution) config.yml: serve TLS
    http:
      addr: :5000
      tls:
        certificate: /certs/registry.example.com.fullchain.pem
        key: /certs/registry.example.com.key
  3. 3.

    Last resort: mark the registry as insecure (daemon.json)

    Listing it under insecure-registries in /etc/docker/daemon.json (and restarting dockerd) lets Docker talk plain HTTP to it. Images and credentials then travel unencrypted and unauthenticated; use it only on isolated lab networks.

    json
    {
      "insecure-registries": ["registry.example.com:5000"]
    }

Related errors

Browse the full signature error database