HAProxy's 200 ms LLM token delay is a smart default. Here's when to change it. An HAProxy engineer investigated a benchmark showing the proxy adds roughly 206 ms of latency to LLM token streams and found the cause is not application-level buffering but the kernel's MSG_MORE flag, which HAProxy sets on client-facing writes while a response body is still being received. The delay is a documented, intentional default that batches small writes into full packets for bulk HTTP traffic, and it can be reversed with a single line of configuration for token-streaming workloads. NOTE: I work at HAProxy, so I'm probably a bit biased. Still, this is an accurate look at the situation. A fun little deep dive, actually. Regardless, this is my own opinion/research, not official from the company. A recent benchmark post https://dev.to/remdore/nginx-streams-your-tokens-fine-haproxy-holds-them-for-206ms-10p2 made the rounds with a table that seems bad for HAProxy. Four proxies sat in front of a scripted LLM emitter sending one tiny frame every 50 milliseconds. Through nginx, Caddy, and Traefik, tokens arrived 2 to 3 milliseconds after the proxy got them, one per read, indistinguishable from no proxy at all. Through HAProxy, the first token showed up 206 milliseconds late and the rest arrived in bursts of five with no gap between them. The post's verdict: HAProxy buffers your tokens. That sounded unusual for such a performant tool, so I decided to dig into it a bit. Here is what I found: nothing in HAProxy is collecting tokens in a bucket . The delay comes from HAProxy telling the kernel to wait for more data, on purpose, documented almost to the millisecond, and reversible with one line of config. And the answer to "why is it on by default" is that for most of the traffic HAProxy carries, the default is the faster choice. The background is Nagle's algorithm https://en.wikipedia.org/wiki/Nagle's algorithm , named for John Nagle and described in RFC 896 back in 1984: a sender holding small amounts of data should wait for a full packet's worth, or for the receiver to acknowledge the previous packet, before putting another tiny packet on the wire. The reasoning is arithmetic. Without batching, a 60-byte write pays a header-sized envelope, and those headers can double the bytes transmitted, or worse as frames shrink. Nagle isn't the actor here, though . HAProxy already sets TCP NODELAY on its TCP sockets. What holds the tokens is a second, explicit mechanism: while a response body is still being received, HAProxy tags every client-facing write with the kernel's MSG MORE flag, which tells the kernel "more data is coming, hold this." In the 3.4 source the article tested, the H1 mux sets the tag on every in-flight body write src/mux h1.c , via CO SFL MSG MORE and clears it when the body ends. The configuration manual https://docs.haproxy.org/2.2/configuration.html 4.2-option%20http-no-delay describes the effect exactly: the system "waits for enough data to be available in order to only send full packets. Typical delays are around 200 ms per round trip." A token stream never fills a packet. Sixty bytes every 50ms, with a round trip sitting between each one, so the corked writes sit in the kernel until the tag clears or a timer cycle releases them. The tokens leave in batches, late by a few cycles, and they reach the client mashed together: five frames, zero gap, 206 ms. That matches the benchmark numbers. First, this is not application buffering. HAProxy forwards the response body as it arrives. The waiting happens in the kernel's send path, not in a proxy buffer. Second, the benchmark author says this himself: frames-per-read is a property of the stream shape, not a permanent fact about the proxy. Same proxy, 1.1 KB frames instead of 60-byte ones, and the delay drops to 53 ms with near-zero coalescing. The batching only bites when frames are small relative to a packet, which is, unfortunately, the exact shape of an LLM token. For the traffic HAProxy was built to carry, batching is a measurable win, and the costs fall on workloads that never notice. HAProxy's home turf is bulk HTTP at high concurrency: API responses, uploads, downloads, and everything in between, at request rates where packet counts and syscall overhead show up directly on the CPU graph. Every tiny write batched into a bigger packet is a syscall saved, a header not transmitted, and a few microseconds returned to the event loop. The HAProxy troubleshooting guide