Saltar a contenido

Kernel TLS (KTLS)

KTLS moves TLS record encryption out of NGINX and into the Linux kernel. Once a session's keys are handed to the kernel, static files are served with SSL_sendfile(): file pages go from the page cache straight to encrypted packets, without ever being copied into user space.

Every GetPageSpeed NGINX build — the stock nginx package, nginx-mod and edge — links against openssl35, which we compile with enable-ktls. There is nothing to install or rebuild: KTLS capability is already in the binary you are running, and it stays completely inactive until you switch it on per server block.

Should you enable it?

KTLS is opt-in for a reason. Our measurements (NGINX 1.30.4, OpenSSL 3.5, TLS 1.3, AES-GCM, static file workload):

  • EL9 stock kernel 5.14, x86_64 — roughly CPU-neutral. The kernel's TLS path and OpenSSL's AES-NI path cost about the same; there is little to gain.
  • Kernel 6.8+ — a large win: about a third less NGINX worker CPU and substantially higher throughput on the same hardware, thanks to SSL_sendfile() and a faster in-kernel crypto path.

The benefit is concentrated in TLS-heavy static file serving (downloads, package repositories, media). Proxied responses do not use SSL_sendfile() and gain much less. KTLS applies to TCP only — HTTP/3 (QUIC) is unaffected.

Do not enable on Rocky/RHEL 9.7 kernel 5.14.0-611.24.1.el9_7

This specific EL 9.7 kernel corrupts data sent through KTLS sendfile(): clients receive files whose checksums do not match. We found this during release validation (our OpenSSL test suite carries a skip for exactly this kernel). Since stock EL9 gains ~nothing from KTLS anyway, the safe rule is simple: only enable KTLS when running a modern kernel, e.g. kernel-lt / kernel-ml 6.x. Verify integrity after enabling (below) — a checksum comparison over HTTPS takes a minute.

Enabling KTLS

Requirements: the tls kernel module (present in EL distro kernels and our kernel-lt/kernel-ml builds; it auto-loads on first use) and TLS 1.2/1.3 with an AES-GCM or ChaCha20-Poly1305 cipher.

In the server block where you want it:

server {
    listen 443 ssl;
    ...
    sendfile on;
    ssl_conf_command Options KTLS;
}

sendfile on; is usually already inherited from the http block; it is what unlocks the zero-copy SSL_sendfile() path.

ssl_conf_command inheritance

ssl_conf_command directives are inherited from the http level only when the server block defines none of its own. If you already use ssl_conf_command at the http level, repeat those commands in any server block where you add Options KTLS.

Verifying

The kernel counts KTLS sessions and errors:

$ cat /proc/net/tls_stat
TlsCurrTxSw    3
TlsTxSw        412
TlsTxSwError   0
TlsDecryptError 0
...

TlsTxSw climbing while you make HTTPS requests confirms KTLS transmit is active (TlsRxSw stays 0 — NGINX only offloads the send side). TlsTxSwError and TlsDecryptError must stay at 0.

Then confirm integrity end-to-end — this is the check that catches a bad kernel:

$ curl -so /tmp/f https://your.server/some-large-file
$ sha256sum /tmp/f   # compare against the file on the server

If checksums mismatch, disable KTLS and update the kernel before re-trying.