Error message
tls: client offered only unsupported versions: [301]
net/http writes it as "http: TLS handshake error from <client address>: tls: client offered only unsupported versions: [301]"; the list shows every version the client offered (300 = SSL 3.0, 301 = TLS 1.0, 302 = TLS 1.1).
Go crypto/tls server (net/http ErrorLog) — the client offered only TLS versions below the server's minimum
What it means
A Go TLS server received a ClientHello that offered only protocol versions below its minimum, here TLS 1.0 (version code 301). Go servers accept TLS 1.2 and 1.3 by default, so the handshake is refused with a protocol_version alert and net/http logs this line. The client on the other end is outdated or deliberately probing.
Why it happens
Outdated clients
commonLegacy devices, old Java 6/7 or .NET Framework applications, and embedded HTTP clients that cannot do TLS 1.2.
Internet scanners and security tools
commonScanners deliberately offer old versions to classify servers; such lines from many different addresses are expected on public endpoints.
Clients configured for an old version
occasionalApplications with a protocol setting pinned to TLS 1.0 for a long-gone legacy integration.
How to fix it
- 1.
Find out who sends them
Group the log lines by client address: a few known addresses mean outdated clients to upgrade; many unknown addresses mean scanners.
bash # Which addresses send these handshakes, and how often? journalctl -u myservice --since today | grep 'client offered only unsupported versions' \ | grep -oE 'from [0-9a-f.:]+' | sort | uniq -c | sort -rn | head - 2.
Keep TLS 1.2 as the server minimum
Do not lower MinVersion to accommodate scanners; upgrade the few legitimate old clients instead.
go // Keep TLS 1.2 as the minimum (Go's server default); set it explicitly to document intent srv := &http.Server{Addr: ":443", TLSConfig: &tls.Config{MinVersion: tls.VersionTLS12}} - 3.
Separate handshake noise
http.Server.ErrorLog receives handshake errors; directing it to a dedicated logger keeps application logs readable.
go // Route handshake noise from scanners to a separate logger instead of the main log srv.ErrorLog = log.New(handshakeLogWriter, "tls: ", 0)