yum upgrades for production use, this is the repository for you.
Active subscription is required.
Technical Briefing: NGINX Dynamic TLS Records — An Honest Benchmark in 2026
The Problem
NGINX’s default ssl_buffer_size is 16 KB, and each TLS record of that size straddles roughly 12 TCP segments. On a lossy link, if a single segment drops, the receiver cannot deliver any byte of that record until retransmission completes — adding at least one full RTT to TTFB on a connection mid-slow-start.
Cloudflare’s 2015 ngx_http_tls_dyn_size patch (shipped in nginx-mod) was built to address this by ramping record size from one segment up to the full 16K over the connection’s lifetime. Cloudflare’s own production has since reverted to a static 4K record. Their README states: “What we do now: We use a static record size of 4K. This gives a good balance of latency and throughput.”
The Benchmark
A controlled benchmark rebuilt the patch and tested it against stock NGINX defaults: 200 cold HTTP/2 connections per condition, randomized interleave, CPU-pinned client and server, tc netem delay 50ms loss 1%.
Results (all times in ms)
- Median TTFB is identical across all four configurations (medians 412–413 ms — the cost of the TLS 1.3 handshake plus one round trip). On requests that don’t see a packet drop, the patch is invisible — no “first paint improvement” on the median user.
- p95 TTFB swings 5–30%, much of it inside the noise band. Under proper isolation, dyn_rec defaults are net negative on large file delivery.
- Static 4K approximately ties baseline 16K on tail latency (10 KB TTFB p95: 707 vs 647; 1 MB TTFB p95: 607 vs 608; 1 MB Total p95: 1424 vs 1343) and is ~20% better on 100 KB Total p95 (825 vs 1043).
- The only consistent winner: dyn_rec with tuned settings (
threshold=10,size_hi=16384— skip the middle stage, ramp to full record size after 10 small records) wins p95 TTFB on 100 KB by 28% and ties everything else on 10 KB and 1 MB. A real but narrow win. - No configuration helps the p99 tail. Every condition’s p99 TTFB sits between 1426 and 1542 ms — the case where two or more segments drop on the same response. Dynamic record sizing helps when one segment drops; it does not help when retransmits compound.
Recommended Fixes, Ranked by Leverage
- Migrate to HTTP/3. QUIC frames data at the packet layer — no TCP, so no TLS-record-straddling-segments problem. The whole problem class disappears. This is the actual fix for mobile and last-mile cellular workloads.
ssl_buffer_size 4k;in thehttp {}block — one line, no patched binary, matches what Cloudflare runs in production, approximately ties the patch in the bench. Described as the highest-leverage single-line change for HTTPS-over-TCP tail latency in 2026.net.ipv4.tcp_notsent_lowat = 16384andnet.ipv4.tcp_congestion_control = bbrvia/etc/sysctl.d/99-tls-tail.conf. Cloudflare’s 2018 follow-up measured first-paint improvements much larger than the TLS record sizing patch ever produced; both knobs target the same workload at the TCP layer where the problem actually lives.- Brotli compression — a response that fits inside
ssl_buffer_sizeafter Brotli never crosses a record boundary. Brotli quality 6 typically halves payload sizes vs gzip. - Data-driven tuning via
ngx_http_tuningv1.3.0 — a passive observer that builds histograms of header sizes, body sizes, and response times and emits a JSON recommendation. As of v1.3.0 the recommendation block includesssl_buffer_size, derived from the same response body distribution it tracks forproxy_buffers. If p95 response body fits in 4 KB it recommendsREDUCEto4k; if most responses exceed one TLS record it staysOKat16k. For bimodal traffic an optionaldyn_rec_advisorystring points nginx-mod users at thessl_dyn_rec_*directives — but stock nginx never sees those directives in the always-emitted snippet, songinx_config.snippetis always copy-paste deployable on the upstream binary. - The patch itself, only in its tuned profile:
ssl_dyn_rec_enable on; ssl_dyn_rec_size_lo 1369; ssl_dyn_rec_size_hi 16384; ssl_dyn_rec_threshold 10; ssl_dyn_rec_timeout 5s;— only worth enabling for 50 KB–200 KB responses on lossy links. Expect 20–28% p95 TTFB reduction on mid-size responses, no measurable change anywhere else, and nothing on the median user.
Patch Directive Behavior
Verified against the C source and at runtime:
- After 40 records the connection bumps to 4229 bytes per record; after 80 records it goes to full
ssl_buffer_size. - NGINX-MOD ships the patch but disables it by default. With
ssl_dyn_rec_enable off(the default) the patch costs nothing — same code paths as stock nginx. - Negative test confirmed:
ssl_dyn_rec_enable on;inside alocation {}block fails withnginx: [emerg] "ssl_dyn_rec_enable" directive is not allowed here.
Installation (Tuning Module)
RHEL/Rocky/Alma/CentOS Stream/Amazon Linux/Fedora:
sudo dnf install https://extras.getpagespeed.com/release-latest.rpm
sudo dnf install nginx-module-tuning
Debian/Ubuntu:
curl -sSL https://nginx-extras.getpagespeed.com/setup.deb.sh | sudo bash
sudo apt-get install nginx-module-tuning
The package drops /usr/share/nginx/modules/tuning.conf with the load_module line; add include /usr/share/nginx/modules/*.conf; at the top of /etc/nginx/nginx.conf. Then tuning_advisor on; plus a status endpoint on 127.0.0.1:8080 with tuning_advisor_status;, allow 127.0.0.1;, deny all;. Reload, observe a few hours of real traffic, then curl http://127.0.0.1:8080/tuning-advisor | jq . and paste nginx_config.snippet into http {}.
Benchmark Reproducibility
The full script reruns on a clean Rocky Linux 10 VM in about 35 minutes. It:
- Installs nginx-mod (
sudo dnf swap nginx nginx-mod) - Pins the worker to CPU 0 (
worker_processes 1;plusworker_cpu_affinity 0001;) - Drops in four server blocks on ports 8443 (baseline 16K), 9443 (dyn_rec defaults), 9444 (dyn_rec tuned), 9445 (static 4K)
- Generates 10k/100k/1M test files
- Applies
tc qdisc add dev lo root netem delay 50ms loss 1% - Runs a shuffled 2,400-request plan via
taskset -c 1 curl -ks --http2, aggregating with Python’sstatistics.medianand an index lookup at[int(n*0.95)]for p95
Bottom Line
The patch is a well-engineered solution to a real problem — in 2015, when TCP initial congestion windows were smaller, HTTP/3 didn’t exist, and last-mile loss was higher and more correlated. In 2026, with IW10 universal, HTTP/3 production-ready on every major browser, and Cloudflare themselves on static 4K, dynamic TLS records is a niche optimization for mid-size responses on lossy links, and only with non-default tuning. The maintainers considered dropping it from nginx-mod entirely but kept it because the default-off patch costs nothing.
Separately
TLS drift is silent — a working cipher list today can be an A- grade tomorrow. GetPageSpeed Amplify runs scheduled gixy scans across every host and ties findings to live NGINX runtime metrics; it is drop-in compatible with the deprecated nginx-amplify-agent (EOL January 2026).
Read the full article: NGINX Dynamic TLS Records: An Honest Benchmark in 2026
