GrepBoardLog

Drop log files anywhere

Files, whole folders and .gz archives. Or press Ctrl+V to paste text.

HAProxy HTTP log: timers and termination states

An HAProxy log line tells you more than most access logs: how long the request waited in a queue, how long the TCP connect took, how long the server thought, and β€” in four letters β€” who ended the stream and in which phase. This page is the reference for reading it during an incident.

Drop a haproxy.log on this page and the board decodes the termination states into plain language, draws each request as queue / connect / server / transfer bars, and builds the frontend β†’ backend β†’ server map from the log alone. Nothing is uploaded.

The HTTP log format

option httplog in a frontend turns on the HTTP format. The manual defines it as this exact log-format string:

# strict equivalent of "option httplog"
log-format "%ci:%cp [%tr] %ft %b/%s %TR/%Tw/%Tc/%Tr/%Ta %ST %B %CC \
            %CS %tsc %ac/%fc/%bc/%sc/%rc %sq/%bq %hr %hs %{+Q}r"

A line as it lands in a file through syslog:

Oct 18 10:02:02 lb1 haproxy[2201]: 198.51.100.7:40110 [18/Oct/2026:10:01:57.020] fe_http be_api/api-3 0/0/0/120/5032 200 65536 - - SD-- 315/301/44/15/0 0/0 "GET /api/export HTTP/1.1"
#awk fieldFieldIn the exampleMeaning
–$1–$4syslog headerOct 18 10:02:02 lb1Added by syslog, not by HAProxy. Its width decides every awk position below.
1$5process_name[pid]:haproxy[2201]:Process name and pid.
2$6client_ip:client_port198.51.100.7:40110Client of the TCP connection. With accept-proxy and the PROXY protocol, the forwarded address.
3$7[request_date][18/Oct/2026:10:01:57.020]When the first byte of the request was received (%tr). Not when the line was written.
4$8frontend_namefe_httpFrontend that accepted the connection.
5$9backend/serverbe_api/api-3Backend chosen and the last server tried. <NOSRV> when no server was reached; <STATS> for the stats page.
6$10TR/Tw/Tc/Tr/Ta0/0/0/120/5032Timers in milliseconds β€” next section.
7$11status_code200Status returned to the client. Usually the server's, but HAProxy sets it when the server could not be reached or the response was blocked.
8$12bytes_read65536Bytes sent to the client, headers included. A 64-bit counter.
9$13captured_request_cookie-From "capture cookie"; a dash when not configured.
10$14captured_response_cookie-Same, for the response.
11$15termination_stateSD--Who ended the stream, in which phase, and what happened to the persistence cookie.
12$16actconn/feconn/beconn/srv_conn/retries315/301/44/15/0Concurrent connections on the process, frontend, backend and server when logged, then retries.
13$17srv_queue/backend_queue0/0How many requests were ahead of this one in the server queue and the backend queue.
14–15–{captured headers}(absent)From "capture request/response header". The braces only appear when capture is configured, and the content can contain spaces.
16last"http_request""GET /api/export HTTP/1.1"Request line, always last and always quoted. Can be truncated if the request is longer than the syslog buffer.

Reading the example: the server answered headers after 120 ms (Tr), then the connection to the server died with an error while the body was flowing (SD). The client got a 200 with 64 KiB of a response that was meant to be larger, 5032 ms after the request arrived. The status says success; the termination state says the download broke.

When the awk positions shift

The positions above assume the classic syslog header (Mmm dd hh:mm:ss host). An RFC 5424 or ISO-8601 header has a different number of words in front, and so does a log forwarded with an extra hostname. Captured headers only move fields after the queue counters. When in doubt, find the timer field (five numbers separated by slashes) and count from there; the one-liner below that finds the timer field does exactly that. The board does not care: it finds the client address and the request date and reads from there, whatever the header.

The five timers: TR / Tw / Tc / Tr / Ta

All values are milliseconds. They follow one request through the proxy:

first byte of the request                         last byte of the response
|                                                                         |
|-- TR --|-- Tw --|-- Tc --|-------- Tr --------|---------- Td ----------|
 request   queue    TCP      server works until    response body goes
 headers   wait     connect  response headers      out to the client
|<------------------------------- Ta ------------------------------------>|

Before TR: Th (TCP accept + TLS / PROXY handshakes) and Ti (idle, keep-alive)
Tq = Th + Ti + TR      Tt = Th + Ti + Ta      Td = Ta - (TR + Tw + Tc + Tr)
TimerWhat it measures-1 meansLarge values usually mean
TRTime to receive the full request headers, from the first byte to the empty line ending them.End of headers never seen: client closed early or timed out.Network trouble between client and HAProxy, or a request typed by hand.
TwTime spent in the backend and server queues waiting for a connection slot.Request killed before reaching the queue: invalid or denied.Servers at their maxconn; requests wait their turn.
TcTime to establish the TCP connection to the server (SYN to SYN/ACK).The connection to the server never established.Network trouble between HAProxy and the server; should be about 1 ms on a LAN.
TrServer response time: from connection established until the server sent complete response headers.Response headers never seen, most often because "timeout server" struck.The server is slow on this request. For POST, includes the time the client took to send the body.
TaTotal active time: first byte of the request to last byte of the response.Never -1.Slow transfer, a slow client, or a stream that hit a timeout. A "+" prefix means option logasap logged it early.

Timer patterns that point at the cause

PatternWhat happenedCheck
-1/xx/xx/xx/TaThe client did not send a complete request in time, or aborted.Termination state, then "timeout http-request" and "timeout client".
TR/-1/xx/xx/TaThe request could not be processed: servers down, invalid request, or denied by an ACL.Termination state.
TR/Tw/-1/xx/TaConnection to the server failed: refused, or timed out after Ta-(TR+Tw) ms.Termination state, then "timeout connect".
TR/Tw/Tc/-1/TaThe server accepted the connection but did not return a full response in time, or closed after Ta-(TR+Tw+Tc) ms.Termination state, then "timeout server".
values near 3000, 6000, 9000TCP retransmissions: TCP retransmit delays are 3, 6, 12… seconds, so a timer close to a multiple of 3 s is almost always a lost packet.Th/Ti near 3000: client side. Tc near 3000: HAProxy to server.
Ta close to a configured timeoutThe stream was probably aborted by that timeout.Which timeout matches, and the termination state.

Th, Ti, Td, Tt, Tq, Tu

  • Th: TCP accept plus TLS and PROXY protocol handshakes. Reported only on the first request of a connection; later requests show 0.
  • Ti: idle time before the request β€” time a pre-connected or keep-alive connection sat waiting. -1 if nothing was received.
  • Td: transfer time of the response body, after the headers. Not in the default format; compute it as Ta βˆ’ (TR + Tw + Tc + Tr), leaving out timers that are -1.
  • Tt: whole stream duration, accept to close. For HTTP, Tt = Th + Ti + Ta.
  • Tq: Th + Ti + TR. Older HAProxy versions logged Tq/Tw/Tc/Tr/Tt in the same position. Because Tq and Tt include handshake and idle time, they look much larger on keep-alive connections; the manual recommends TR over Tq.
  • Tu: estimated time seen by the user β€” the stream duration without the idle time Ti.

Termination state: the four letters

Field 11 is two characters in TCP logs and four in HTTP logs. The first two say how the stream ended; the last two describe the persistence cookie, and are -- when cookies are not used. So the SD in a search for "SD--" is the part that matters, and cD-- is cD with no cookie involved.

First character: what ended the stream

CharMeaning
CThe client aborted the TCP session unexpectedly.
SThe server aborted the TCP session unexpectedly, or explicitly refused it.
PThe proxy aborted the stream prematurely: connection limit, a deny rule, or a security check on the response.
LThe stream was handled locally by HAProxy (redirect, return, stats, cache…).
RA proxy resource ran out: memory, sockets, source ports. Treat as a serious anomaly.
IInternal error found by a self-check. Should never happen; the manual asks for it to be reported as a bug.
DKilled because the server was detected down and is configured to kill connections when going down.
UKilled on a backup server because an active server came back up and backup connections are configured to be killed.
KKilled by an administrator.
cClient-side timeout expired while waiting for the client to send or receive data.
sServer-side timeout expired while waiting for the server to send or receive data.
-Normal completion: both sides closed with nothing left in the buffers.

Second character: the phase it was in

CharPhase
RWaiting for a complete, valid request from the client (HTTP only). Nothing was sent to a server.
QWaiting in the queue for a connection slot (servers with maxconn). Without a redispatch, no connection attempt was made.
CWaiting for the connection to the server to establish.
HWaiting for complete, valid response headers from the server (HTTP only).
DData phase.
LSending the last data to the client after the server had finished. Rare.
TThe request was tarpitted; the time held open is reported in Tw.
-Normal completion after the end of the transfer.

Third and fourth characters: the persistence cookie

CharThird: cookie from the clientFourth: what happened to the cookie in the response
NNo cookie provided.No cookie from the server, none inserted.
IInvalid cookie, matching no known server.No cookie from the server; HAProxy inserted one.
DCookie pointing at a server that was down.The server's cookie was deleted by HAProxy.
VValid cookie; sent to that server.–
EValid cookie, but expired by "maxidle"; ignored.–
OValid cookie, but too old for "maxlife"; ignored.–
UCookie present but not used (for example a use-server rule chose).HAProxy updated the last-seen date in the cookie (insert mode with maxidle).
P–Cookie provided by the server and passed as is.
R–Cookie provided by the server and rewritten by HAProxy.
-Does not apply (no cookie in the configuration).Does not apply.

Termination state reference: common combinations

The combinations the manual lists, what they mean in plain words, and where to look next. Uppercase is an abort or error, lowercase is a timeout.

StateWhat happenedCheck next
--Normal termination.Nothing on the proxy side. A 5xx with -- came from the server itself β€” read the application log.
CCThe client gave up while HAProxy was still connecting to the server.Tc and the server's health: this happens when the server is recently dead or unchecked.
CDThe client aborted during the data transfer.Usually browsers and mobile clients going away. Many on one path: large downloads, or a middlebox cutting connections.
cDThe client sent and acknowledged nothing for "timeout client" during the data phase.Client-side network loss or clients leaving uncleanly. Compare Ta with timeout client.
CHThe client aborted while waiting for the server to start responding.Tr on these lines: the server was slow, or the user hit Stop.
cH"timeout client" struck while waiting for client data during a POST.Oversized TCP MSS on PPPoE paths, or timeout client shorter than timeout server with a slow server.
CQThe client aborted while queued for a server slot.Tw and the queue fields: servers saturated or one server slow.
CRThe client aborted before sending a full request. Status likely 400.Hand-typed requests, browser pre-connects, or an IDS killing connections. "option http-ignore-probes" hides zero-byte connections.
cR"timeout http-request" struck before a full request arrived. Status likely 408.Browser pre-connect, MSS problems, slow clients. Same "option http-ignore-probes" trade-off.
CTThe client aborted while tarpitted.Make sure the tarpit rules are not catching valid requests.
LCHandled locally: redirect from a "redir" server parameter.Expected behaviour.
LRHandled locally: redirect, http-request return, or an applet (stats, cache, Prometheus exporter, Lua).Expected behaviour.
LHThe response was handled locally: redirect or http-response return.Expected behaviour.
SCThe server, or a device in front of it, refused the TCP connection (RST or ICMP). Status likely 502 or 503.Is the server process listening? Firewall rules? Look at retries in field 12.
sC"timeout connect" struck before the connection to the server completed. Status likely 503 or 504.Network path to the server, the server's accept backlog, the timeout connect value.
SDThe connection to the server died with an error during the data transfer: RST from the server or ICMP from a device on the way.Server crash or network device at that timestamp. The response the client received is truncated.
sDThe server sent and acknowledged nothing for "timeout server" during the data phase.L4 devices in front of the server (firewalls, load balancers) with shorter idle timeouts; keep-alive sessions expiring first on HAProxy.
SHThe server aborted before sending complete response headers, or crashed while processing.Server logs: rare, so the logged requests often reproduce an application bug.
sH"timeout server" struck before the server returned response headers. The most common anomaly.Slow application or saturated database. Tr is -1 and Ta is close to timeout server. Raising the timeout only hides it.
sQThe request waited too long in the queue and expired."timeout queue" and "timeout connect"; server maxconn; bursts often mean I/O or database congestion on the servers.
PCThe proxy refused to connect to the server because the process socket limit was reached.Global maxconn, and ulimit-n if it was forced by hand.
PDThe proxy blocked an incorrectly formatted chunked message after the server sent its headers.Invalid chunked encoding from the server (chunk sizes up to 2 GB βˆ’ 1 are accepted).
PHThe proxy blocked the server's response: invalid, incomplete, dangerous, or matched a filter. The client gets a 502.Header names with forbidden characters. A failed response header rewrite gives a 500 instead (tune.maxrewrite).
PRThe proxy blocked the client's request: invalid syntax (400) or a deny rule (403).Which deny rule matched; a failed request header rewrite gives a 500.
PTThe proxy blocked and tarpitted the request, then returned a 500. Nothing reached a server.Tw shows how long the connection was held.
RCA local resource ran out (memory, sockets, source ports) before the server connection could be made.System tuning; the error output says which resource was missing.

Cookie combinations

Last twoMeaning
--Persistence cookie not enabled.
NNNo cookie from the client, none inserted (for example insert mode with postonly on a GET).
NINo cookie from the client, one inserted β€” a first request in insert mode. Counting these counts new users.
VNValid cookie from the client, none inserted. Most returning requests.
VUValid cookie with an outdated last-visit date; an updated cookie was sent.
IICookie for an unknown server; a valid one was inserted. Typical after removing a server from the configuration.
EICookie too old for maxidle; ignored and replaced.
OICookie too old for maxlife; ignored and replaced.
DIThe cookie's server was down; a new server was chosen and advertised.
VIThe cookie's server was not marked dead but could not be reached; redispatched and the new server advertised.

Users complaining that they have to log in again? Look at II, DI, VI, EI and OI around that time: each one is a request that left its original server.

Connection counters, retries and queues

FieldMeaningWhat to look for
actconnConcurrent connections on the whole process.Close to 512 or 1024 while connection errors happen: the process is probably out of file descriptors.
feconnConcurrent connections on the frontend.Big jumps: backend congestion, sometimes an attack; or frontend maxconn reached.
beconnConcurrent connections on the backend, including queued ones.Estimates how many more servers the load needs.
srv_connConcurrent connections still active on the server. Never above the server maxconn.Often at maxconn: maxconn too low or not enough servers. Only one server high: that server is slow.
retriesConnection retries for this stream. Normally 0.Frequent: network trouble or a full backlog on the server. A "+" prefix means a redispatch happened, so the count does not belong to the logged server.
srv_queueRequests processed before this one in the server queue.Tw divided by it roughly estimates the server response time.
backend_queueRequests processed before this one in the backend queue.Divided by a server's maxconn, roughly the number of missing servers.

What the board shows for HAProxy

  • Termination states: counts per state, each with its plain-language meaning, abnormal ones first.
  • Request anatomy: TR, Tw, Tc, Tr and the transfer time as stacked bars, so you see whether time goes to the queue, the connect or the server.
  • Topology from logs: frontend β†’ backend β†’ server drawn from fields 4 and 5, line width by traffic, red where errors come from.
  • Retries and queue waiting, per backend and server.
  • Look first: for example "HAProxy SD 61 times β€” backend be_api, server api-3".
  • The usual cards too: status codes, top paths, top clients, latency, timeline.

Shell one-liners for HAProxy logs

Tested against a sample option httplog file with the classic syslog header. With another header, add or subtract the same offset from every field number, or use the one that finds the timer field.

  • All termination states, most frequent first.

    awk '{print $15}' haproxy.log | sort | uniq -c | sort -rn
  • Only the first two characters (ignore cookie flags).

    awk '{print substr($15, 1, 2)}' haproxy.log | sort | uniq -c | sort -rn
  • Same, without trusting positions: finds the timer field and takes the fifth field after it.

    awk '{for (i = 1; i <= NF; i++) if ($i ~ /^-?[0-9]+\/-?[0-9]+\/-?[0-9]+\/-?[0-9]+\/\+?[0-9]+$/) {print $(i+5); next}}' haproxy.log | sort | uniq -c | sort -rn
  • Abnormal terminations per backend/server.

    awk 'substr($15, 1, 1) != "-" {print substr($15, 1, 2), $9}' haproxy.log | sort | uniq -c | sort -rn
  • Status codes per backend.

    awk '{split($9, b, "/"); print b[1], $11}' haproxy.log | sort | uniq -c | sort -rn
  • Requests where the server took over a second to send headers (Tr), with the state.

    awk '{split($10, t, "/"); if (t[4] > 1000) print t[4], $9, $15}' haproxy.log | sort -rn
  • Requests that waited in a queue (Tw > 0), with the queue positions.

    awk '{split($10, t, "/"); if (t[2] > 0) print t[2], $9, $17}' haproxy.log | sort -rn
  • Requests where the connection to the server never established (Tc = -1).

    awk '{split($10, t, "/"); if (t[3] == -1 && t[2] != -1) print $9, $11, $15}' haproxy.log | sort | uniq -c
  • Requests with retries; a "+" means redispatched.

    awk '{split($16, c, "/"); if (c[5] != "0") print c[5], $9, $15}' haproxy.log
  • Requests that never reached a server.

    grep -c '/<NOSRV> ' haproxy.log
  • p99 of Ta, total active time.

    awk '{split($10, t, "/"); sub(/^\+/, "", t[5]); print t[5]}' haproxy.log | sort -n | awk '{a[NR]=$1} END {i=int(NR*0.99); if (i<NR*0.99) i++; print a[i] " ms"}'
  • Highest process-wide connection count seen.

    awk '{split($16, c, "/"); if (c[1] > m) m = c[1]} END {print "peak actconn", m}' haproxy.log
  • Longest body transfer times (Td = Ta βˆ’ TR βˆ’ Tw βˆ’ Tc βˆ’ Tr).

    awk '{split($10, t, "/"); if (t[1] >= 0 && t[2] >= 0 && t[3] >= 0 && t[4] >= 0) print t[5]-(t[1]+t[2]+t[3]+t[4]), $9}' haproxy.log | sort -rn | head -3

FAQ

What does SD-- mean in HAProxy logs?

The connection to the server died with an error during the data transfer: HAProxy received an RST from the server or an ICMP message from a device in between. The status may still be 200, but the client got a truncated response. Check the server and the network path at that timestamp.

What does cD-- mean?

The client-side timeout expired during the data phase: the client neither sent nor acknowledged data for as long as "timeout client". Usually client network loss or a client that went away without closing.

What does sH-- mean?

"timeout server" expired before the server sent its response headers. It is the most common anomaly and points at a slow application or database. On these lines Tr is -1 and Ta is close to the timeout server value.

What do CR-- and cR-- mean?

Both are about a request that never fully arrived. CR: the client aborted first, status likely 400. cR: "timeout http-request" struck first, status likely 408. Browser pre-connects cause many of them; "option http-ignore-probes" stops logging zero-byte connections.

What do TR, Tw, Tc, Tr and Ta mean?

TR: time to receive the request headers. Tw: time in queues. Tc: time to connect to the server. Tr: time until the server sent its response headers. Ta: total active time from the first request byte to the last response byte. All in milliseconds.

Why is a timer -1?

That phase never completed. -1 in Tc means the server connection never established; -1 in Tr means the response headers never came; -1 in TR means the request headers never completed. Ta is never -1.

What does a + in front of the retries count mean?

The stream was redispatched to another server after the maximum retries on the first one. The retries then belong to the first server, not the one shown in the log.

What is <NOSRV>?

The request ended before any server was chosen or reached: rejected, redirected, handled locally, or the client gave up early. The termination state says which.

My logs show Tq/Tw/Tc/Tr/Tt. Is that the same?

That is the layout of older HAProxy versions. Tq includes the handshake and idle time, and Tt is the whole stream duration, so on keep-alive connections they are larger than TR and Ta. The board parses the old layout too but shows the values under the new names, so read its TR as Tq and its Ta as Tt for those files.

πŸ”’ Nothing leaves your browser.

Your logs are parsed and counted right here, on your machine.