Fixing proxyconnect tcp: tls: first record does not look like a TLS handshake

One letter: the proxy URL says https://, the proxy port speaks plain HTTP. It shows up in Go programs, Docker pulls and every other tool built on Go's net/http.

4 min readTested on Go 1.27 · crane
With HTTPS_PROXY=https:// Go sends a TLS handshake to a plain HTTP proxy port and fails with proxyconnect tcp: tls: first record does not look like a TLS handshake; with http:// the same request returns 200.One letter in HTTPS_PROXYHTTPS_PROXY=https://USER:PASS@via.proxies.vision:9000TLSHTTP/1.1 …proxyconnect tcp: tls: first record does not look like a TLS handshakeHTTPS_PROXY=http://USER:PASS@via.proxies.vision:9000200 { "country": "DE", … }Go 1.27 · crane · October 22, 2026

In short

Change the proxy URL from https:// to http://, in your code or in HTTPS_PROXY. The name of the variable means “proxy for https:// sites”, not “talk TLS to the proxy”. Your traffic to the website stays encrypted.

HTTPS_PROXY=http://…socks5:// for SOCKS ports

Go’s net/http reads the proxy URL’s scheme to decide how to reach the proxy. With https:// it starts a TLS handshake with the proxy before anything else. An HTTP proxy port doesn’t speak TLS; it answers the bytes it got with a plain-text HTTP error, and Go stops at the first byte:

txt
Get "https://ipinfo.io/json": proxyconnect tcp: tls: first record does not look like a TLS handshake

proxyconnect is Go telling you the failure is on the way to the proxy; the website was never contacted. The same request, one letter apart:

tls: first record…HTTPS_PROXY=https://USER:PASS@via.proxies.vision:9000TLS to a plain HTTP port
tls: first record…https_proxy=https://… (lower case)Go reads both spellings
200HTTPS_PROXY=http://USER:PASS@via.proxies.vision:9000CONNECT, then TLS to the site
200HTTPS_PROXY=USER:PASS@via.proxies.vision:9000no scheme: Go assumes http://

Other languages fail on the same mistake with their own words: Python and curl say wrong version number. The same cause across clients.

Most people meet this message pulling an image. The Docker daemon, registry clients, kubectl, Terraform and Helm are written in Go and take their proxy from the same settings. We pulled an image digest with crane, a Go registry client, through our gateway:

errorHTTPS_PROXY=https://… crane digest alpine:3.22Get "https://index.docker.io/v2/": proxyconnect tcp: tls: first record…
sha256:d406…HTTPS_PROXY=http://… crane digest alpine:3.22digest fetched

Fix it where the daemon reads it

docker pull is done by the daemon, not the docker command, so the variable in your shell doesn’t count. Set the proxy with http:// where the daemon takes it: the proxies section of its daemon.json, or the service environment, then restart it. Docker Desktop has the same field under Settings, Resources, Proxies.

The other proxyconnect errors

Go 1.27 net/http against our gateway, https://ipinfo.io as the target.

Proxy URLError
https://…:9000proxyconnect tcp: tls: first record does not look like a TLS handshake
https://…:11000proxyconnect tcp: unexpected EOF
http://…:11000malformed HTTP response "\x01\x01"
http://…:8080proxyconnect tcp: dial tcp …:8080: connect: connection refused
http://…:9000, wrong or no loginProxy Authentication Required
http://…:9000, -country-usathe gateway's reason text for 403
socks5://…:11000200
socks5h://…:11000200
Fig. 1 — October 22, 2026. Our gateway takes HTTP on 9000 and SOCKS5 on 11000.

Proxy Authentication Required

Go returns the proxy’s reason phrase as the error, without the code. This one is a 407: the login is missing or wrong. In code, put the login into the URL with url.UserPassword, which encodes special characters for you. Go proxy authentication in detail.

403 and 465

Also returned as reason text only. On our gateway 403 is a value in the username it can’t read (a three-letter country, a session on the rotating port), 465 means no exit matches every filter. curl shows the numbers if you need them.

SOCKS ports need socks5://

Go speaks SOCKS5 natively: socks5:// and socks5h:// both worked on port 11000. http:// to that port produced the two odd errors in the table.

A setup that works

go run . with Go 1.27; PROXY_USER and PROXY_PASS from the environment. Or set HTTPS_PROXY=http://… and keep http.ProxyFromEnvironment.

main.go
package main

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"time"
)

func main() {
	// http://, not https://: the proxy port speaks plain HTTP. Your request to an
	// https:// site is still encrypted end to end inside the CONNECT tunnel.
	proxy := &url.URL{
		Scheme: "http",
		User:   url.UserPassword(os.Getenv("PROXY_USER")+"-country-de", os.Getenv("PROXY_PASS")),
		Host:   "via.proxies.vision:9000",
	}
	client := &http.Client{
		Transport: &http.Transport{Proxy: http.ProxyURL(proxy)},
		Timeout:   30 * time.Second,
	}

	resp, err := client.Get("https://ipinfo.io/json")
	if err != nil {
		fmt.Println(err) // "proxyconnect tcp: ..." means the proxy itself, not the website
		os.Exit(1)
	}
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	fmt.Println(resp.StatusCode, string(body))
}
txt
200 {
  "ip": "92.208.23.216",
  "city": "Essen",
  "region": "North Rhine-Westphalia",
  "country": "DE",
  …
What does 'proxyconnect tcp: tls: first record does not look like a TLS handshake' mean?
The proxy URL starts with https://, so Go opened a TLS connection to the proxy. The proxy port speaks plain HTTP and answered in clear text, which Go can't read as a TLS handshake. Change the proxy URL (or HTTPS_PROXY) to http://. Requests to https:// sites stay encrypted inside the tunnel.
Why do I see it when pulling Docker images?
The Docker daemon and registry clients are Go programs and use the same proxy code. A proxy configured as https://host:port fails exactly like this. We reproduced it with crane, a Go registry client: https:// in HTTPS_PROXY gave this error for index.docker.io, http:// fetched the image digest.
Isn't https:// more secure for HTTPS_PROXY?
Only if the proxy itself listens with TLS, which most HTTP proxy ports don't. The variable describes how you reach the proxy. With http:// your client sends CONNECT in clear text, including the proxy login, and then speaks TLS with the website through the tunnel; the website traffic is encrypted either way.
What do the other proxyconnect errors mean?
proxyconnect tcp: dial tcp …: connect: connection refused is a wrong port or host. proxyconnect tcp: unexpected EOF came from https:// pointed at a SOCKS port. malformed HTTP response "\x01\x01" is http:// pointed at a SOCKS port; use socks5:// there. Proxy Authentication Required is the login.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.