Read a HAProxy log line — status code and termination state
Split an option httplog line into fields, read the first two characters of the termination state, then count them and list slow requests with awk.
A frontend with option httplog writes one line per request. The output below is real log output from HAProxy 2.8.16 hit with curl, and the field descriptions come from configuration manual sections 8.2.3 and 8.5.
[1] Split a normal request line into fields. This line has no syslog header; it was received with log stdout format raw local0.
203.0.113.7:37660 [19/Oct/2026:12:17:20.987] web ok/s1 0/0/0/5/5 200 185 - - ---- 1/1/0/0/0 0/0 "GET /index.html HTTP/1.1"- 1
203.0.113.7:37660— client address:port - 2
[…]— time the first byte of the request arrived - 3
web— frontend name - 4
ok/s1— backend/server.<NOSRV>if no server was reached - 5
0/0/0/5/5— timersTR/Tw/Tc/Tr/Ta(milliseconds) - 6
200— status code returned to the client - 7
185— bytes sent to the client, headers included - 8·9
- -— captured cookies - 10
----— termination state - 11
1/1/0/0/0—actconn/feconn/beconn/srv_conn/retries - 12
0/0— server queue/backend queue - 13
"GET …"— request line
[2] Read the timers. TR is the time to receive the full request, Tw the queue wait, Tc the server connection, Tr the wait for the server's response headers, and Ta the total time the request stayed in HAProxy. A phase that was never reached shows -1. Look at Tr for server delay.
[3] The first two of the four termination characters give the cause and the stage. A normal session is ----.
## First character — who ended it- `-` normal completion- `C` client aborted · `c` client-side timeout- `S` server aborted or refused · `s` server-side timeout- `P` proxy blocked it (limit · deny rule · security check)- `L` handled locally by HAProxy (redirect · return · stats)- `R` proxy resource exhausted · `I` internal error ## Second character — which stage- `R` waiting for the request · `Q` queue- `C` connecting to the server · `H` waiting for the server's response headers- `D` data phase · `L` sending the last data- `T` tarpit · `-` normal end after the data transfer[4] Check the cases where the server is unusable. A request sent to a closed port is refused, so it logs SC-- and 503. A response slower than timeout server (2 s) logs sH-- and 504. Tr is -1 on both lines.
203.0.113.7:37674 [19/Oct/2026:12:17:21.013] web dead/s1 0/0/-1/-1/3007 503 217 - - SC-- 1/1/0/0/3 0/0 "GET /dead HTTP/1.1"203.0.113.7:37538 [19/Oct/2026:12:17:24.033] web slow/s1 0/0/0/-1/2003 504 198 - - sH-- 1/1/0/0/0 0/0 "GET /slow HTTP/1.1"[5] Check the requests that never reached a server. <NOSRV> means no server answered. http-request return logs LR--, http-request deny logs PR--, and a connection that sent only part of a request logs cR--.
203.0.113.7:37554 [19/Oct/2026:12:18:00.736] web web/<NOSRV> 0/-1/-1/-1/0 403 84 - - LR-- 1/1/0/0/0 0/0 "GET /deny HTTP/1.1"203.0.113.7:38422 [19/Oct/2026:12:18:00.747] web web/<NOSRV> 0/-1/-1/-1/0 403 192 - - PR-- 1/1/0/0/0 0/0 "GET /block HTTP/1.1"203.0.113.7:45032 [19/Oct/2026:12:18:16.812] web web/<NOSRV> -1/-1/-1/-1/2003 408 0 - - cR-- 2/2/0/0/0 0/0 "<BADREQ>"[6] Count requests per termination state. With no syslog header the termination state is field 10. To list only abnormal lines, use the condition $10 != "----".
$ awk '{c[$10]++} END{for(k in c) print c[k], k}' access.log | sort -rn13 ----2 sH--2 SC--1 PR--1 LR--[7] Find requests with a large Ta. Ta is the fifth value of field 5 split on /.
$ awk '{split($5,t,"/"); print t[5], $6, $10, $4}' access.log | sort -rn | head -33007 503 SC-- dead/s13006 503 SC-- dead/s12003 504 sH-- slow/s1A large Ta with SC-- or sH-- points at the backend server first, not the application code.
Analyze your HAProxy log in the browser ↗ — nothing is uploaded.