golang/go · error
tls: server sent a ServerHello extension forbidden in TLS 1.
Error message
tls: server sent a ServerHello extension forbidden in TLS 1.3
What it means
Thrown in checkServerHelloOrHRR() when the TLS 1.3 ServerHello contains legacy extensions forbidden by RFC 8446: OCSP stapling, session ticket support, extended_master_secret, secure_renegotiation, ALPN protocol, or SCTs. In TLS 1.3 these belong in EncryptedExtensions or separate post-handshake messages, not in the ServerHello itself.
Source
Thrown at src/crypto/tls/handshake_client_tls13.go:191
if hs.serverHello.supportedVersion != VersionTLS13 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
}
if hs.serverHello.vers != VersionTLS12 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server sent an incorrect legacy version")
}
if hs.serverHello.ocspStapling ||
hs.serverHello.ticketSupported ||
hs.serverHello.extendedMasterSecret ||
hs.serverHello.secureRenegotiationSupported ||
len(hs.serverHello.secureRenegotiation) != 0 ||
len(hs.serverHello.alpnProtocol) != 0 ||
len(hs.serverHello.scts) != 0 {
c.sendAlert(alertUnsupportedExtension)
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
}
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server did not echo the legacy session ID")
}
if hs.serverHello.compressionMethod != compressionNone {
c.sendAlert(alertDecodeError)
return errors.New("tls: server sent non-zero legacy TLS compression method")
}
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
if hs.suite != nil && selectedSuite != hs.suite {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
}
if selectedSuite == nil {View on GitHub (pinned to b6b368adc5)
Solutions
- Server must follow RFC 8446 section 4.2 — these extensions must not appear in the TLS 1.3 ServerHello; they belong in EncryptedExtensions or separate messages.
- Verify no TLS-terminating proxy (nginx, HAProxy, AWS ALB) is injecting legacy extensions.
- Update the server or proxy software to a TLS 1.3-compliant version.
- If unfixable, restrict client to TLS 1.2 to avoid the TLS 1.3 extension checks.
Defensive patterns
Strategy: try-catch
Try / catch
conn, err := tls.Dial("tcp", addr, config)
if err != nil {
if strings.Contains(err.Error(), "extension forbidden in TLS 1.3") {
// Server sends legacy extensions — fall back to TLS 1.2
config.MaxVersion = tls.VersionTLS12
conn, err = tls.Dial("tcp", addr, config)
}
} Prevention
- Check for TLS-terminating proxies that inject legacy extensions.
- Update server and proxy software to TLS 1.3-compliant versions.
- Use testssl.sh or SSL Labs to scan server TLS configuration.
- Maintain TLS 1.2 fallback for non-compliant servers.
When it happens
Trigger: Triggered when any of the following are set on hs.serverHello: ocspStapling, ticketSupported, extendedMasterSecret, secureRenegotiationSupported, non-empty secureRenegotiation, non-empty alpnProtocol, or non-empty scts. The client sends alertUnsupportedExtension.
Common situations: Server that incorrectly includes TLS 1.2-style extensions in a TLS 1.3 ServerHello. TLS-terminating proxy or middlebox injecting legacy extensions. Server implementation that shares extension-handling code between TLS 1.2 and 1.3 without filtering. Misconfigured reverse proxy.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- tls: server selected TLS 1.3 using the legacy version field
- tls: server selected an invalid version after a HelloRetryRe
- tls: server sent an incorrect legacy version
- tls: server did not echo the legacy session ID
- tls: server sent an unnecessary HelloRetryRequest message
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/66bef008b95fd4e0.
Report an issue: GitHub.