{"record":{"id":"013e4051fea7a63d","repo":"golang/go","slug":"tls-encrypted-client-hello-cannot-be-used-pre-tls","errorCode":null,"errorMessage":"tls: Encrypted Client Hello cannot be used pre-TLS 1.3","messagePattern":"tls: Encrypted Client Hello cannot be used pre-TLS 1\\.3","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server.go","lineNumber":209,"sourceCode":"\tc.vers, ok = c.config.mutualVersion(roleServer, c.quic != nil, clientVersions)\n\tif !ok {\n\t\tc.sendAlert(alertProtocolVersion)\n\t\treturn nil, nil, fmt.Errorf(\"tls: client offered only unsupported versions: %x\", clientVersions)\n\t}\n\tc.haveVers = true\n\tc.in.version = c.vers\n\tc.out.version = c.vers\n\n\t// This check reflects some odd specification implied behavior. Client-facing servers\n\t// are supposed to reject hellos with outer ECH and inner ECH that offers 1.2, but\n\t// backend servers are allowed to accept hellos with inner ECH that offer 1.2, since\n\t// they cannot expect client-facing servers to behave properly. Since we act as both\n\t// a client-facing and backend server, we only enforce 1.3 being negotiated if we\n\t// saw a hello with outer ECH first. The spec probably should've made this an error,\n\t// but it didn't, and this matches the boringssl behavior.\n\tif c.vers != VersionTLS13 && (ech != nil && !ech.inner) {\n\t\tc.sendAlert(alertIllegalParameter)\n\t\treturn nil, nil, errors.New(\"tls: Encrypted Client Hello cannot be used pre-TLS 1.3\")\n\t}\n\n\treturn clientHello, ech, nil\n}\n\nfunc (hs *serverHandshakeState) processClientHello() error {\n\tc := hs.c\n\n\ths.hello = new(serverHelloMsg)\n\ths.hello.vers = c.vers\n\n\tfoundCompression := false\n\t// We only support null compression, so check that the client offered it.\n\tfor _, compression := range hs.clientHello.compressionMethods {\n\t\tif compression == compressionNone {\n\t\t\tfoundCompression = true\n\t\t\tbreak\n\t\t}","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server.go#L191-L227","documentation":"The server negotiated a TLS version below 1.3 while observing an outer Encrypted Client Hello (ECH) extension. Per the ECH specification and BoringSSL behavior mirrored here, ECH is only valid with TLS 1.3; using outer ECH with an older version is illegal_parameter. Backend servers accepting inner ECH at 1.2 are tolerated, but outer ECH at < 1.3 is rejected.","triggerScenarios":"In the server's clientHello processing, c.vers != VersionTLS13 and an ECH extension was decoded with ech.inner == false (i.e. the outer SNI was encrypted). The client offered ECH but negotiated an older version.","commonSituations":"A client that sends ECH but advertises max_version of TLS 1.2, a downgrade forced by a middlebox stripping supported_versions, or a misconfigured client library combining ECH with legacy version caps.","solutions":["Ensure the client's tls.Config does not cap MaxVersion below VersionTLS13 when ECH is enabled.","Investigate middleboxes or proxies that may strip the TLS 1.3 supported_versions entry.","Update the client TLS stack to a version that correctly pairs ECH with TLS 1.3 negotiation.","If ECH is not required, disable it on the client to allow fallback to a standard 1.2 handshake."],"exampleFix":"// before\ncfg := &tls.Config{\n    MaxVersion: tls.VersionTLS12, // forces downgrade; ECH becomes illegal\n    // ECH configured via ECHConfig list\n}\n\n// after\ncfg := &tls.Config{\n    MinVersion: tls.VersionTLS13,\n    MaxVersion: tls.VersionTLS13,\n}","handlingStrategy":"validation","validationCode":"// Client: when enabling ECH, also require TLS 1.3.\nif len(echConfigList) > 0 {\n    cfg.MinVersion = tls.VersionTLS13\n    cfg.MaxVersion = tls.VersionTLS13 // or omit, but never < 1.3\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"Encrypted Client Hello cannot be used pre-TLS 1.3\") {\n    cfg.MaxVersion = 0 // allow 1.3\n    // retry handshake\n}","preventionTips":["Pair ECH configuration with MinVersion >= TLS 1.3.","Audit middleboxes for supported_versions stripping.","Document that ECH implies TLS 1.3 in your config."],"tags":["tls","server-handshake","ech","encrypted-client-hello","version-negotiation","tls13"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}