golang/go · error

tls: client using inappropriate protocol fallback

Error message

tls: client using inappropriate protocol fallback

What it means

RFC 7507 defines TLS_FALLBACK_SCSV (0x5600), a cipher value a client includes to signal an intentional version downgrade. If the server observes it AND the negotiated version (c.vers) is lower than the server's maximum supported version, the downgrade is treated as a possible attack and aborted with an inappropriate_fallback alert. This is a downgrade-attack defense.

Source

Thrown at src/crypto/tls/handshake_server_tls13.go:137

		return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
	}

	// Abort if the client is doing a fallback and landing lower than what we
	// support. See RFC 7507, which however does not specify the interaction
	// with supported_versions. The only difference is that with
	// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
	// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
	// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
	// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
	// supported_versions was not better because there was just no way to do a
	// TLS 1.4 handshake without risking the server selecting TLS 1.3.
	for _, id := range hs.clientHello.cipherSuites {
		if id == TLS_FALLBACK_SCSV {
			// Use c.vers instead of max(supported_versions) because an attacker
			// could defeat this by adding an arbitrary high version otherwise.
			if c.vers < c.config.maxSupportedVersion(roleServer, c.quic != nil) {
				c.sendAlert(alertInappropriateFallback)
				return errors.New("tls: client using inappropriate protocol fallback")
			}
			break
		}
	}

	if len(hs.clientHello.compressionMethods) != 1 ||
		hs.clientHello.compressionMethods[0] != compressionNone {
		c.sendAlert(alertIllegalParameter)
		return errors.New("tls: TLS 1.3 client supports illegal compression methods")
	}

	hs.hello.random = make([]byte, 32)
	if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
		c.sendAlert(alertInternalError)
		return err
	}

	if len(hs.clientHello.secureRenegotiation) != 0 {

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Fix the root cause preventing the higher version (middlebox/firewall blocking TLS 1.3, broken TLS 1.3 implementation on either side)
  2. If the server genuinely only supports the lower version, ensure the client does not send TLS_FALLBACK_SCSV
  3. Verify tls.Config max supported version is what you intend (don't accidentally cap it below the client's offer)
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify your server supports the version the client wants to fall back from.
maxVer := cfg.maxSupportedVersion(roleServer, false) // conceptual
_ = maxVer // ensure it is not artificially capped

Try / catch

if err := tlsConn.Handshake(); err != nil {
    if strings.Contains(err.Error(), "inappropriate protocol fallback") {
        // Likely a middlebox blocking the higher version, or an attack.
        log.Printf("downgrade/fallback rejected from %v: %v", remote, err)
    }
    c.Close()
    return
}

Prevention

When it happens

Trigger: The client's cipher suites include TLS_FALLBACK_SCSV while c.vers < server's maxSupportedVersion. E.g., a client retry loop that steps down from TLS 1.3 to TLS 1.2 and tags the retry with FALLBACK_SCSV, hitting a server that genuinely supports TLS 1.3.

Common situations: Client libraries that auto-retry with version downgrade + FALLBACK_SCSV (Chrome, Firefox, some HTTP clients); a firewall or middlebox blocking TLS 1.3 so the client falls back; or an active attacker injecting SCSV to probe downgrade behavior.

Understand the failure class

Related errors


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