Zum Inhalt

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.

We run this setup in production: extras.getpagespeed.com, the repository these packages come from, has served its HTTPS (TCP) downloads over KTLS since September 2026 (NGINX on kernel-lt 6.18, about 6 million KTLS sessions in the first month). Kernel transmit errors stayed at zero and served files matched their on-disk checksums.

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. With OpenSSL 3.5 the receive side is offloaded too, so TlsRxSw climbs alongside it.

TlsTxSwError must stay at 0. TlsDecryptError counts records from clients that failed authentication on receive: on an Internet-facing server, scanners and broken clients sending garbage make it creep up slowly (on our own package repository: under 300 across 6 million sessions in a month, nearly all from a single scanner IP). A steady trickle is normal. A jump that follows legitimate traffic is not.

New [alert] lines in the error log

With KTLS receive active, a client that sends a TLS alert or a non-data record while NGINX reads the request (typically a scanner) shows up as recv() failed (5: Input/output error) at [alert] level. Before KTLS, the same disconnects either logged nothing or logged at info. They are harmless: the connection was ending anyway. On our repository server none of them came from a package-manager download.

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.