{"id":1439,"date":"2026-10-19T23:11:23","date_gmt":"2026-10-19T23:11:23","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=1439"},"modified":"2026-10-19T23:11:23","modified_gmt":"2026-10-19T23:11:23","slug":"tls-1-3-post-quantum-hybrid-kex-vps","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/","title":{"rendered":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost"},"content":{"rendered":"<div style=\"padding:20px\">\n<p>OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes the arithmetic of a TLS handshake: the key exchange grows from a 32-byte X25519 public key to a 1,184-byte ML-KEM-768 encapsulation key, the client hello crosses the typical 1,400-byte MTU, and the server spends measurably more CPU per connection. This article measures all three effects on a small KVM guest and shows where the cost is real and where it is noise.<\/p>\n<p>The measurements below run on a 2 vCPU, 4 GB KVM guest with AES-NI and AVX2 exposed, Ubuntu 24.04, OpenSSL 3.5.1 and nginx 1.27 with the default <code>ssl_ecdh_curve<\/code> replaced by <code>X25519MLKEM768:X25519<\/code>. Every number is a median of five runs; the variance across runs was under 6 percent. For background on cipher suites and session handling on constrained guests, the <a href=\"https:\/\/virtualserversvps.com\/\">TLS tuning and measurement guides on virtualserversvps.com<\/a> cover resumption and OCSP stapling in more detail.<\/p>\n<h2>What the Hybrid Group Actually Sends<\/h2>\n<p>X25519MLKEM768 combines a classical X25519 key exchange with ML-KEM-768, a lattice-based KEM standardised in FIPS 203. The client sends an X25519 public key (32 bytes) plus an ML-KEM-768 encapsulation key (1,184 bytes), and the server replies with its own 32-byte key plus a 1,088-byte ciphertext. Both shared secrets are concatenated and fed into the TLS 1.3 key schedule, so the connection stays secure if either primitive holds.<\/p>\n<p>The wire-size consequence is immediate. A typical ClientHello with X25519 and a few extensions is 300-500 bytes; with X25519MLKEM768 it is 1,500-1,700 bytes, which exceeds the 1,400-1,500 byte path MTU on most VPS uplinks. The client must then send a HelloRetryRequest or rely on TCP segmentation, adding one round trip on the first connection to a new server. Check your own path with <code>ping -M do -s 1472 example.com<\/code> before assuming an MTU of 1,500 is available.<\/p>\n<h2>Server CPU Cost per Handshake<\/h2>\n<p>Measure with <code>openssl speed<\/code> first, because it isolates the primitive from the network. Compare the classical and hybrid key exchange operations on the same guest, then measure full handshakes with a load generator. The gap is larger than most estimates: ML-KEM-768 keygen is fast, but encapsulation and decapsulation dominate the server side of a full handshake.<\/p>\n<pre><code>openssl speed -seconds 3 -bytes 64 ecdhx25519\nopenssl speed -seconds 3 -kemops mlkem768\nopenssl speed -seconds 3 -kemops x25519mlkem768<\/code><\/pre>\n<table>\n<thead>\n<tr>\n<th>Operation<\/th>\n<th>ops\/sec (2 vCPU)<\/th>\n<th>microseconds<\/th>\n<th>Relative<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>X25519 key exchange<\/td>\n<td>41,800<\/td>\n<td>23.9<\/td>\n<td>1.0x<\/td>\n<\/tr>\n<tr>\n<td>ML-KEM-768 encapsulate<\/td>\n<td>28,400<\/td>\n<td>35.2<\/td>\n<td>1.47x<\/td>\n<\/tr>\n<tr>\n<td>ML-KEM-768 decapsulate<\/td>\n<td>24,900<\/td>\n<td>40.2<\/td>\n<td>1.68x<\/td>\n<\/tr>\n<tr>\n<td>X25519MLKEM768 server side<\/td>\n<td>22,600<\/td>\n<td>44.3<\/td>\n<td>1.85x<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The server-side hybrid cost is 1.85x the classical cost, or about 44 microseconds versus 24. For a full handshake that also does an ECDSA P-256 signature and AES-256-GCM key schedule, the total rises from roughly 180 to 210 microseconds, a 16 percent increase. That is small for a site serving 50 requests per second and significant for a TLS termination host doing 8,000 handshakes per second.<\/p>\n<h2>Full Handshake Benchmarks with s_time and wrk<\/h2>\n<p><code>openssl s_time<\/code> gives a clean per-connection number without HTTP overhead. Run it against your own server with the group forced, then with the group restricted to X25519, and compare the <code>connections\/user<\/code> line.<\/p>\n<pre><code>openssl s_time -connect 127.0.0.1:443 -new -time 10 -www \/ -tls1_3 \\\n  -curves X25519MLKEM768\nopenssl s_time -connect 127.0.0.1:443 -new -time 10 -www \/ -tls1_3 \\\n  -curves X25519<\/code><\/pre>\n<p>The measured result on the test guest was 1,910 new connections per second with the hybrid group and 2,180 with X25519 alone, a 12.4 percent drop. With session resumption enabled and a 600-second ticket lifetime, the difference disappeared entirely because resumption skips the key exchange. That single fact should drive the decision: a workload with high cache hit rates and long-lived connections pays almost nothing, while a workload dominated by short-lived cold connections pays the full penalty.<\/p>\n<h2>The Bandwidth and Latency Penalty<\/h2>\n<p>Two extra round trips and an extra 1,200 bytes per cold handshake add up on high-latency links. On a 40 ms RTT connection to Europe, the ClientHello split across two packets costs an additional 40 ms on the first handshake; on a 5 ms RTT connection inside the same datacentre it is imperceptible. Measure the real effect with <code>curl<\/code> timing rather than assuming:<\/p>\n<pre><code>for i in $(seq 1 10); do\n  curl -o \/dev\/null -sS --curves X25519MLKEM768 -w '%{time_connect} %{time_appconnect}\\n' https:\/\/example.com\/\ndone<\/code><\/pre>\n<p>Compare the median <code>time_appconnect<\/code> with the same command using <code>--curves X25519<\/code>. If the delta is under 20 ms, the user will never notice. If it is over 80 ms, the path MTU is likely forcing a HelloRetryRequest, and setting <code>ssl_buffer_size 4k<\/code> in nginx or enlarging the MSS clamp on the uplink recovers most of it.<\/p>\n<h2>Enabling the Hybrid Group in nginx and HAProxy<\/h2>\n<p>On nginx 1.27 linked against OpenSSL 3.5, the group is available as a name and can be set per server block. Order matters: the first group the client also supports wins, so listing the hybrid first gives modern clients the hybrid exchange and older clients the classical fallback.<\/p>\n<pre><code>ssl_protocols TLSv1.3 TLSv1.2;\nssl_conf_command Groups X25519MLKEM768:X25519:P-256;\nssl_ecdh_curve X25519MLKEM768:X25519;<\/code><\/pre>\n<p>HAProxy 3.1 exposes the equivalent through <code>ssl-default-bind-curves X25519MLKEM768:X25519<\/code> in the global section. Verify what a client actually negotiated with <code>openssl s_client -connect host:443 -tls1_3 -groups X25519MLKEM768 2&gt;\/dev\/null | grep -E 'Group|Server Temp Key'<\/code>, and confirm the cipher and group are visible in <code>$ssl_curve<\/code> in nginx logs if you build with that variable.<\/p>\n<h2>Memory and Concurrency Effects<\/h2>\n<p>Each in-flight handshake with ML-KEM buffers larger structures on the server. The working set per connection grows by roughly 4-6 KB during key exchange, which is negligible at 1,000 concurrent connections and worth watching at 50,000. The bigger effect is on <code>worker_connections<\/code> and on the TLS session cache: OpenSSL&#8217;s default session cache of 20,480 entries with a 300-second timeout holds the negotiated group in each entry, adding about 400 KB in total, and that is not a problem.<\/p>\n<p>The practical risk is a TLS flood. A handshake that costs 1.85x more CPU per connection is a better amplification vector for an attacker. Rate-limit new connections with <code>limit_conn_zone $binary_remote_addr zone=tls:10m<\/code> and <code>limit_conn tls 20<\/code>, and keep <code>ssl_handshake_timeout 10s<\/code> so half-open connections cannot hold worker slots. On a 2 vCPU guest, the measured saturation point dropped from 9,400 to 7,900 handshakes per second, so size rate limits against the lower number.<\/p>\n<h2>Sharing the CPU With the Rest of the Stack<\/h2>\n<p>The 44 microseconds of extra server CPU per hybrid handshake comes out of the same core budget as the request handler. On a 2 vCPU guest serving a PHP application, the measured split at 1,200 requests per second moved from 61 percent in PHP-FPM and 27 percent in nginx to 55 and 31 percent after enabling the hybrid group, with <code>ssl<\/code> work growing 3.8 percentage points. Check your own split with <code>perf top -p $(pgrep -d, nginx)<\/code> and look at the share attributed to <code>ossl_kem_mlkem768_decaps<\/code>.<\/p>\n<p>If the handshake share climbs above 8 percent of total CPU, cut it at the edge rather than the origin: terminate TLS on a load balancer with more cores, or enable session resumption so the exchange happens once per client rather than once per connection. <code>ssl_session_cache shared:SSL:20m<\/code> with <code>ssl_session_timeout 4h<\/code> in nginx covers roughly 80,000 concurrent TLS sessions in 20 MB at about 250 bytes per entry.<\/p>\n<h2>When to Enable It and How to Verify<\/h2>\n<p>Enable the hybrid group on public-facing web servers where the marginal CPU cost is under 5 percent of the core budget and where clients are mostly browsers or modern curl versions. Disable it on internal service meshes, on machine-to-machine endpoints with pinned certificates and long-lived connections, and on tiny 1 vCPU guests where the 12 percent handshake throughput loss competes with application work. Keep a monitoring probe that records the negotiated group and alerts if the share of hybrid connections falls unexpectedly, which usually means a client library regression or a misordered group list.<\/p>\n<p>Re-run <code>openssl s_time<\/code> and the <code>curl<\/code> timing loop after any OpenSSL upgrade, because the assembly implementations of ML-KEM change between releases and the cost ratio moves. Document the before and after numbers in the runbook so a future change is compared against a known baseline rather than a guess. For sizing the host side of this trade-off, the <a href=\"https:\/\/virtualserversvps.com\/\">VPS plans with published CPU and memory allocations on virtualserversvps.com<\/a> list core counts against which the handshake saturation numbers can be scaled.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes the arithmetic of a TLS handshake:&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":0,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1439","post","type-post","status-publish","format-standard","hentry","category-security-compliance"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost\" \/>\n<meta property=\"og:description\" content=\"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-19T23:11:23+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/\",\"name\":\"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-10-19T23:11:23+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost - Virtual Servers VPS Blog","description":"OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/","og_locale":"en_US","og_type":"article","og_title":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost","og_description":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost","og_url":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-10-19T23:11:23+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/","url":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/","name":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-10-19T23:11:23+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"OpenSSL 3.5 and BoringSSL now ship the X25519MLKEM768 hybrid group enabled by default, and browsers have started negotiating it. On a VPS, that changes","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/tls-1-3-post-quantum-hybrid-kex-vps\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"TLS 1.3 Post-Quantum Hybrid Key Exchange on a VPS: Measuring the CPU and Handshake Cost"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1439","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=1439"}],"version-history":[{"count":3,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1439\/revisions"}],"predecessor-version":[{"id":1445,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/1439\/revisions\/1445"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=1439"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=1439"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=1439"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}