Enabling hybrid post-quantum key exchange is usually a one-line change, provided the TLS library underneath supports ML-KEM. This guide covers the four most common servers. We tested the nginx configuration below against OpenSSL 3.5 in our own integration tests.
Step 0: check the TLS library
ML-KEM groups are built into OpenSSL 3.5 and later. Check the version your server actually uses, which may differ from the command-line tool:
openssl version
nginx -V 2>&1 | grep -o 'OpenSSL [0-9.]*'
apachectl -V | grep -i openssl
haproxy -vv | grep -i openssl
If the version is older than 3.5, upgrade the operating system packages or use a build linked against a recent OpenSSL. Debian 13 (trixie) ships OpenSSL 3.5, for example. Without it, the directives below will fail or be ignored.
nginx
In the server block (or http block), allow TLS 1.3 and put the hybrid group first, followed by classical fallbacks:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;
Despite its name, ssl_ecdh_curve sets the list of key exchange groups passed to OpenSSL. Reload with nginx -t && systemctl reload nginx.
Apache HTTP Server
With mod_ssl linked to OpenSSL 3.5 or later, pass the group list directly to OpenSSL:
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLOpenSSLConfCmd Groups X25519MLKEM768:X25519:prime256v1
Place the directives in the virtual host or in the global SSL configuration, then run apachectl configtest and reload.
HAProxy
In the global section, set the default groups for all bind lines:
global
ssl-default-bind-options ssl-min-ver TLSv1.2
ssl-default-bind-curves X25519MLKEM768:X25519:P-256
You can also set curves on an individual bind line. HAProxy must be built with OpenSSL 3.5 or later; check haproxy -vv.
Caddy
Caddy uses the Go TLS stack, which enables X25519MLKEM768 by default from Go 1.24. Recent Caddy releases built with Go 1.24 or later therefore negotiate it without any configuration. If your Caddyfile restricts curves in a tls block, add the hybrid group explicitly:
tls {
curves x25519mlkem768 x25519
}
Behind a CDN or a load balancer
Enable the group on the component that terminates TLS for visitors: the CDN, the cloud load balancer or the reverse proxy. Then do the same for the connection from that component to your origin servers, which is a separate TLS link and often forgotten.
Keep a fallback
Always keep a classical group such as X25519 after the hybrid one. Clients that do not support ML-KEM simply negotiate X25519, so nothing breaks. The hybrid ClientHello is about one kilobyte larger; a few old middleboxes have mishandled large handshakes in the past, which is another reason to keep the fallback and to monitor errors after the change.
Verify
openssl s_client -connect www.example.com:443 -servername www.example.com \
-groups X25519MLKEM768 -brief </dev/null | grep 'Negotiated'
You should see Negotiated TLS1.3 group: X25519MLKEM768. You can also run the PQC Status scanner, which additionally checks TLS versions, cipher suites and the certificate chain. Record the change and its date in your cryptographic inventory.
Which group should you choose?
X25519MLKEM768 is the default choice: recommended by IANA and supported by all major browsers. If you follow ANSSI guidance for sensitive systems, which prefers the highest security level, add SecP384r1MLKEM1024 first for clients that support it. Pure ML-KEM groups without a classical component are not recommended by ANSSI or BSI, which both ask for hybrid mode.