TLS 1.3 only at the edge, the secure way
The audit wants TLS 1.2 gone. You set the minimum version, the scanner still grades you on a cipher list, and someone asks why AES-128 shows up when you only listed AES-256. Envoy is not ignoring you; it is ignoring the setting you picked.
The short answer
Set tls_minimum_protocol_version to TLSv1_3 on the Envoy listener, or tls.minVersion "1.3" in an Envoy Gateway ClientTrafficPolicy. TLS 1.2 clients then fail with a protocol_version alert. Do not use cipher_suites for TLS 1.3: Envoy applies it to TLS 1.2 and below only. If you must pin TLS 1.3 ciphers, use a compliance policy.
On this page
What goes wrong
Envoy accepts TLS 1.2 by default on every listener. So does Envoy Gateway. TLS 1.2 is not broken when configured well, but it keeps the older handshake, the older cipher negotiation and no post-quantum key exchange at all: every ML-KEM group is TLS 1.3 only. A client that can be pushed to TLS 1.2 gets classical ECDHE.
The usual fix is a minimum version. The usual mistake that follows is the
cipher list. People set cipher_suites (Envoy) or ciphers (Envoy Gateway)
to AES-256 only and expect TLS 1.3 to follow. It does not. Envoy's TLS
library, BoringSSL, does not let you configure TLS 1.3 cipher suites, and
Envoy documents the field as TLS 1.2 and below.
The last trap is the client base. Turning off TLS 1.2 locks out every client that cannot do TLS 1.3. For a public site in 2026 that is a small group, but it includes old Android devices, Java 8 without updates, and embedded clients like payment terminals or monitoring probes. Find them before they find you.
What the docs say
Minimum TLS protocol version. By default, it's TLSv1_2 for both clients and servers.
Source: Envoy docs, TlsParameters
If specified, the TLS listener will only support the specified cipher list when negotiating TLS 1.0-1.2 (this setting has no effect when negotiating TLS 1.3).
Source: Envoy docs, TlsParameters
Min specifies the minimal TLS protocol version to allow. The default is TLS 1.2 if this is not specified.
Source: Envoy Gateway API reference, ClientTLSSettings
The docs are clear about the cipher list. They do not say how to restrict
TLS 1.3 ciphers instead. The answer is the compliance_policies field,
covered on the CNSA 2.0 page.
The secure configuration
Envoy Gateway:
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: tls13-only
namespace: edge
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: edge
tls:
minVersion: "1.3" # refuse TLS 1.0, 1.1 and 1.2
# Add ecdhCurves here for post-quantum key exchange; see the
# ML-KEM-1024 on Envoy Gateway page.Envoy Gateway attaches one ClientTrafficPolicy per Gateway. If the Gateway
already has one, merge these fields into it; a second policy is refused with
Accepted=False (see ML-KEM-1024 on Envoy Gateway).
Standalone Envoy, the listener's TLS context:
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_params:
tls_minimum_protocol_version: TLSv1_3 # refuse TLS 1.2 and older
tls_maximum_protocol_version: TLSv1_3
# No cipher_suites: it has no effect on TLS 1.3. To pin TLS 1.3
# to AES-256-GCM, use compliance_policies (see the CNSA 2.0 page).
tls_certificates:
- certificate_chain: {filename: /etc/envoy/tls/edge.crt}
private_key: {filename: /etc/envoy/tls/edge.key}Before you deploy, look at your access logs for the TLS version in use.
Envoy's %DOWNSTREAM_TLS_VERSION% access log operator records it per
request.
Prove it
The test in secure-tests/tls-1-3-edge/run.sh runs three listener settings
on Envoy 1.39.1 and probes each with OpenSSL 3.5.8: a TLS 1.3 client, a TLS
1.2 client, and a TLS 1.3 client that offers only AES-128-GCM.
openssl s_client -connect edge.example.com:443 -tls1_3
openssl s_client -connect edge.example.com:443 -tls1_2
openssl s_client -connect edge.example.com:443 -tls1_3 -ciphersuites TLS_AES_128_GCM_SHA256== Envoy defaults
-tls1_3 TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
-tls1_2 TLSv1.2, Cipher is ECDHE-ECDSA-CHACHA20-POLY1305
-tls1_3 AES-128-GCM only TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
== tls_minimum_protocol_version: TLSv1_3
-tls1_3 TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
-tls1_2 refused: alert protocol version
-tls1_3 AES-128-GCM only TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256
== TLSv1_3 plus cipher_suites: [ECDHE-ECDSA-AES256-GCM-SHA384]
-tls1_3 TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
-tls1_2 refused: alert protocol version
-tls1_3 AES-128-GCM only TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256The third block is the trap: an AES-256 only cipher_suites list, and a
TLS 1.3 client still gets AES-128-GCM.
The same script renders the Envoy Gateway policy offline with egctl:
== Envoy Gateway v1.9.1: ClientTrafficPolicy tls.minVersion "1.3", rendered offline by egctl
ClientTrafficPolicy Accepted="True": Policy has been accepted.
tlsParams:
tlsMaximumProtocolVersion: TLSv1_3
tlsMinimumProtocolVersion: TLSv1_3Against your own edge, a TLS 1.2 probe should end in alert protocol version:
openssl s_client -connect www.example.com:443 -servername www.example.com -tls1_2 </dev/null 2>&1 | grep -oE "alert [a-z ]+|Protocol *: *TLSv1\.[23]"Mistakes people make
Trusting cipher_suites to shape TLS 1.3
It only shapes TLS 1.2 and below. With a TLS 1.3 minimum, the field does nothing. Remove it so nobody reads it as a control.
Forgetting the second listener
Envoy Gateway applies a ClientTrafficPolicy to the Gateway you target. A second Gateway has its own settings, and a TLS passthrough route never touches Envoy's TLS at all: the backend decides. Scan every public IP and port, not one hostname.
Cutting off clients you did not list
Payment terminals, old Android, Java 8, and uptime monitors are the usual casualties. Check the access log field for TLS 1.2 traffic for two weeks before you switch, and tell the owners of those clients.
Leaving TLS 1.2 on the load balancer in front
If a cloud load balancer terminates TLS before Envoy, its own policy decides. Set it to TLS 1.3 as well, or pass TLS through to Envoy.
Checklist
- Log
%DOWNSTREAM_TLS_VERSION%and review TLS 1.2 traffic before the change. - Set
tls.minVersion: "1.3"on every Gateway (ortls_minimum_protocol_version: TLSv1_3on every Envoy listener). - Remove
cipher_suites/ciphersfrom TLS 1.3 only listeners. - Use
compliance_policiesif you must pin TLS 1.3 ciphers. - Probe with
-tls1_2and expectalert protocol version. - Check load balancers and other listeners on the same IPs.
TLS 1.2 had a long career. Give it a quiet retirement, and read the access log before the party.
H2-CPQE
Learn it on a live range
Post-quantum TLS, in Edge and Post-Quantum Networking: a real host in your browser, and every objective checked on the machine.
Start freeThe Dome
Want it run for you?
The Dome puts post-quantum TLS, a WAF that blocks, signed DNS and a zero-trust mesh in front of your application. Tell us what you run.
See the Dome