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

  1. 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.
  2. Verify no TLS-terminating proxy (nginx, HAProxy, AWS ALB) is injecting legacy extensions.
  3. Update the server or proxy software to a TLS 1.3-compliant version.
  4. 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

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

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/66bef008b95fd4e0. Report an issue: GitHub.