HAProxy SslFrontendKeyRate: the handshake rate that drives CPU cost
SslFrontendKeyRate is the number of new (non-resumed) TLS handshakes per second across your HAProxy frontends. Because full TLS handshakes are the most CPU-intensive operation HAProxy performs, this metric is effectively the CPU cost of your TLS traffic expressed as a rate.
Operators usually look for this metric in two situations: HAProxy CPU is climbing and they need to know whether TLS is the cause, or they are doing capacity planning and need a defensible number for “how many handshakes per second can this box absorb.” Both depend on understanding what the metric counts, where it comes from, and what breaks it.
What it is and why it matters
When a client opens a TLS connection to an SSL-terminated frontend, one of two things happens:
- Full handshake. Certificate validation, key exchange, asymmetric crypto. Expensive on the HAProxy side, burning real CPU per connection.
- Session resumption. The client presents a session ID or ticket from a previous connection, HAProxy validates it against its session cache, and the asymmetric work is skipped. Cheap.
SslFrontendKeyRate counts only the first kind: new keys established per second. Resumed sessions do not increment it. SslRate counts all TLS sessions per second including resumed ones, so the gap between SslRate and SslFrontendKeyRate tells you how much of your TLS load is being handled cheaply.
An example of the gap: a frontend showing SslRate of 10378 with SslFrontendKeyRate of 2361, SslFrontendSessionReuse_pct at 77, and Idle_pct at 42 has roughly three quarters of TLS connections resuming and avoiding full handshake cost. If resumption broke and all 10378 sessions per second became full handshakes, the full-handshake load would rise more than fourfold and Idle_pct would collapse.
Two naming gotchas:
- The field is SslFrontendKeyRate, exactly. There is no
SslKeyRate; scripts written against the wrong name silently grep nothing. - It exists only in
show info, not in theshow statCSV output. The HTTP stats page exposes onlyshow statdata, so this metric never appears there. Any pipeline that only scrapes the CSV or the stats web UI is blind to it.
How it works
HAProxy’s SSL engine consults the session cache or ticket state on every incoming TLS connection. When the cache lookup misses or the client presents nothing to resume, the engine performs a full handshake and the new-key counter increments. show info reports this as a rate:
# Collect the TLS handshake signals from show info
echo "show info" | socat unix-connect:/var/run/haproxy.sock stdio | grep -E "^(SslRate|SslFrontendKeyRate|SslFrontendMaxKeyRate|SslFrontendSessionReuse_pct|SslBackendKeyRate|SslCacheLookups|SslCacheMisses|Idle_pct):"
Adjust the socket path for your install (/run/haproxy/admin.sock is another common location).
The related fields that make the metric interpretable:
| Field | What it is |
|---|---|
SslFrontendKeyRate | New frontend TLS handshakes per second. The CPU cost signal. |
SslFrontendMaxKeyRate | Peak value observed since process start. Useful for spotting bursts you missed. |
SslRate | Total TLS sessions per second, resumed plus new. Compare against key rate for resumption share. |
SslFrontendSessionReuse_pct | Percentage of frontend TLS sessions resumed. The inverse of your full-handshake share. |
SslCacheLookups / SslCacheMisses | Cumulative session cache counters. Hit rate = (Lookups - Misses) / Lookups. There is no SslCacheHits field; you compute it. |
SslBackendKeyRate | New handshakes per second to backends (when server ... ssl is in use). The same CPU cost logic applies on the server side. |
Idle_pct | Percentage of time the event loop waits in poll(). The inverse proxy for HAProxy CPU saturation. |
The core relationship: as SslFrontendKeyRate rises, Idle_pct falls proportionally. A rising key rate with a steady Idle_pct means you have headroom. A rising key rate with Idle_pct trending toward 20 percent means you are consuming your safety margin. Idle_pct below roughly 5 percent means the event loop is saturated and new connections will stall in handshake.
flowchart LR
A[New TLS connections] --> B{Session resumable?}
B -- yes --> C[Session cache hit]
C --> D[Resumed session - low CPU]
B -- no --> E[Full handshake]
E --> F[SslFrontendKeyRate rises]
F --> G[CPU consumed by crypto]
G --> H[Idle_pct falls]
H --> I[Handshake latency and stalls]
C -.->|cache broken or flushed| EWhere it shows up in production
TLS CPU exhaustion is a first-class failure archetype. Under a high rate of new, non-resumed TLS connections, CPU saturates on handshakes. Throughput drops and latency spikes, but HAProxy does not crash; it just cannot complete handshakes fast enough. The composite pattern: SslFrontendKeyRate spiking well above baseline, Idle_pct dropping toward zero, session rate elevated, and backend connect time (ctime) rising because the event loop is too busy to establish backend connections promptly. A distinguishing feature is that backends are healthy throughout: rtime stays normal, errors stay low. The bottleneck is admission, not the application.
Broken session resumption is the common root cause. High SslFrontendKeyRate with a low cache hit ratio means every connection is paying full price. Causes include:
- Session cache too small or disabled (
tune.ssl.cachesizeset to 0 disables it entirely, which some operators do deliberately for mTLS scenarios) - Clients that genuinely cannot resume: very diverse client populations, mobile fleets, one-shot scanners. Low reuse is normal for some workloads; the signal is a drop from your baseline
- TLS library or version bugs that break resumption in specific releases. If SslFrontendSessionReuse_pct collapses with no config change, check the changelog for your exact version.
no-tls-ticketson the bind line disabling ticket-based resumption
Reloads reset everything. SslFrontendKeyRate and the cache counters are process-lifetime values; they reset on every reload. Worse, the in-process session cache is invalidated on reload, so every existing client that reconnects must do a full handshake. Expect a key-rate spike after every reload. Sharing ticket keys on disk across processes (tls-ticket-keys) mitigates the ticket path, but the cache spike still happens. If you reload frequently (dynamic service discovery, ingress controller), you are paying a full-handshake tax on a schedule, and your monitoring sees counter discontinuities on top of it.
The busy-polling caveat. Idle_pct is your inverse CPU proxy only when the event loop actually idles. With busy-polling enabled, Idle_pct sits near zero by design and tells you nothing. In that mode, use show activity run-queue depth or system CPU alongside SslFrontendKeyRate.
Multi-process mode. In nbproc deployments, each process has its own SSL session cache and its own key-rate counter. Resumption across processes is not shared, and per-process rates must be summed to get the fleet picture.
Tradeoffs and when this matters
The metric matters most in three operational contexts:
- TLS capacity planning. Full handshakes per second is the resource you are actually provisioning for, not total sessions. Trend SslFrontendKeyRate at daily peak, pair it with Idle_pct at the same moment, and plan upgrades when peak Idle_pct drops below roughly 30 percent. SslFrontendMaxKeyRate tells you the worst burst the process has seen since start, which is the number your headroom must absorb.
- Diagnosing a CPU climb. When HAProxy CPU rises, the first fork in the road is “TLS or not TLS.” SslFrontendKeyRate answers it in one command. If the key rate tracks the CPU curve, you are in the TLS domain and the next question is resumption health. If the key rate is flat while CPU climbs, look elsewhere: ACL evaluation, compression, Lua.
- Attack and anomaly detection. Renegotiation abuse, handshake-flooding DDoS, and massive surges of unique clients all show up as key-rate excursions before they show up as 5xx. A key rate far above baseline with normal request rates is suspicious by itself.
One honest limitation: the exact sampling window for the rate is not documented. Treat it as a short-window gauge, not a precisely averaged rate, and trend it through a monitoring system rather than trusting single samples.
Signals to watch in production
| Signal | Why it matters | Warning sign |
|---|---|---|
SslFrontendKeyRate | Direct measure of full-handshake CPU cost | Trending toward tested ceiling; spikes without matching traffic growth |
SslFrontendMaxKeyRate | Worst burst since process start | Far above your planned headroom |
SslRate vs SslFrontendKeyRate | Gap shows how much load resumption absorbs | Gap closing: resumption share falling |
SslFrontendSessionReuse_pct | Percentage of sessions avoiding full handshakes | Declining from baseline, or near zero unexpectedly |
SslCacheLookups / SslCacheMisses | Computed cache hit ratio | Miss rate above 50% sustained past the post-reload window |
Idle_pct | Event loop headroom (invalid with busy-polling) | Below 20% sustained; below 5% is saturation |
SslBackendKeyRate | Handshake cost to TLS backends | Rising when backend connection reuse should be stable; often pairs with a broken http-reuse |
Uptime_sec | Detects reloads that reset counters and flush the cache | Drops explain key-rate spikes and counter discontinuities |
A practical alerting shape: page when SslFrontendKeyRate is well above baseline or tested ceiling AND Idle_pct is under 5 percent (or, with busy-polling, show activity run-queue is rising) AND real traffic is degrading, sustained for a couple of minutes past the cold-start window. Ticket when the cache miss rate stays above 50 percent beyond the post-reload transient. Track everything else as baseline.
How Netdata helps
- Netdata collects
show infocontinuously, so SslFrontendKeyRate, SslRate, and the cache counters become time series instead of point-in-time socket dumps. That matters for a metric that is invisible in the CSV stats and the HTTP stats page. - Plotting SslFrontendKeyRate against Idle_pct on one dashboard makes the proportionality directly visible; divergence between the two is itself a diagnostic.
- Counter resets on reload are handled as discontinuities rather than phantom drops, so post-reload handshake storms are readable instead of alarming.
- Correlating SslFrontendSessionReuse_pct with the computed cache hit ratio lets you catch resumption regressions, the slow-failing case where CPU cost per connection rises for weeks before Idle_pct ever alerts.
- Per-instance baselines make key-rate anomalies stand out: a burst that is normal at peak but anomalous at 4 AM is obvious on a trended chart.
Related guides
- HAProxy scur approaching slim: the concurrent-session saturation signal
- HAProxy connect time (ctime) high: network latency and backend accept-queue overflow
- HAProxy 502 Bad Gateway: backend connection and response failures
- HAProxy 503 Service Unavailable: no server is available to handle this request
- HAProxy 504 Gateway Timeout: timeout server and the backend timeout cascade
- HAProxy backend queue building (qcur): requests waiting for a free server slot
- HAProxy connection errors (econ): backend connections refused, timed out, or reset
- HAProxy backend losing servers: active server count and cascade risk
- HAProxy 5xx delta: telling HAProxy-generated errors from backend errors
- HAProxy health checks green but the application is broken: when UP does not mean healthy
- HAProxy health check L4TOUT and L4CON: server marked DOWN and unreachable
- HAProxy health check L7STS: server DOWN on the wrong HTTP status






