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.
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.
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:
Get "https://ipinfo.io/json": proxyconnect tcp: tls: first record does not look like a TLS handshakeproxyconnect is Go telling you the failure is on the way to the proxy; the website was never contacted. The same request, one letter apart:
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:
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 URL | Error |
|---|---|
https://…:9000 | proxyconnect tcp: tls: first record does not look like a TLS handshake |
https://…:11000 | proxyconnect tcp: unexpected EOF |
http://…:11000 | malformed HTTP response "\x01\x01" |
http://…:8080 | proxyconnect tcp: dial tcp …:8080: connect: connection refused |
http://…:9000, wrong or no login | Proxy Authentication Required |
http://…:9000, -country-usa | the gateway's reason text for 403 |
socks5://…:11000 | 200 |
socks5h://…:11000 | 200 |
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.
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))
}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?
Why do I see it when pulling Docker images?
Isn't https:// more secure for HTTPS_PROXY?
What do the other proxyconnect errors mean?
Keep reading
wget Proxy Authentication: https_proxy, 407 and an Ignored HTTPS_PROXY
wget through a proxy with a login: which variables it reads (not HTTPS_PROXY), --proxy-user, what 'Proxy tunneling failed' means, and BusyBox wget.
5 min read · #command-line#authentication
curl: (56) CONNECT tunnel failed, response 407 (Received HTTP code 407 from proxy)
curl (56) 'CONNECT tunnel failed, response 407', or 'Received HTTP code 407 from proxy after CONNECT' in older curl: the cause, the fix, every exit code.
6 min read · #errors#authentication
SSL: WRONG_VERSION_NUMBER With a Proxy: the Fix for Python, Go, Node and curl
The proxy URL says https:// and the proxy speaks plain HTTP. The same mistake and its error message in requests, httpx, Go, Node and curl.
6 min read · #python#go
Residential proxies, pay per GB
HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.