{"id":406,"date":"2026-06-12T03:59:00","date_gmt":"2026-06-12T03:59:00","guid":{"rendered":"https:\/\/virtualserversvps.com\/blog\/?p=406"},"modified":"2026-11-03T22:04:07","modified_gmt":"2026-11-03T22:04:07","slug":"haproxy-timeout-ladder-tcp-http-establish","status":"publish","type":"post","link":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/","title":{"rendered":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to one timeout. On a typical frontend with defaults in place, <code>timeout client<\/code>, <code>timeout server<\/code>, <code>timeout connect<\/code>, <code>timeout http-request<\/code> and <code>timeout http-keep-alive<\/code> all interact, and the one you tuned is frequently not the one firing. Reading <code>show stat<\/code> with the right column tells you in seconds which layer gave up.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The ladder is about who waits for whom. Each timeout is a deadline for a different party: the client, the server, the connection attempt, the header exchange, or an idle keep-alive slot. When two of them disagree, the shorter one wins and the logs blame whichever subsystem closed last. On a 2 GB VPS serving a few hundred requests per second, <a href=\"https:\/\/virtualserversvps.com\/\">a plainly provisioned KVM instance<\/a> is enough as long as these values are set deliberately rather than inherited.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Map each timer to the party that waits<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Write the five timeouts down next to the state they govern. If you cannot say which component is blocked in each state, you will tune by intuition and lose. The usual defaults of 50s for client and server, 10s for connect, and 5s for keep-alive are tuned for a generic web app; they are wrong for APIs with 90-second background jobs and equally wrong for a static site behind a CDN.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Directive<\/th><th>State<\/th><th>Typical 503 signature<\/th><\/tr><\/thead><tbody><tr><td>timeout connect<\/td><td>Backend TCP handshake<\/td><td>sC=1 in stats, rising fast<\/td><\/tr><tr><td>timeout server<\/td><td>Backend is silent<\/td><td>sH=1 with sC=0<\/td><\/tr><tr><td>timeout client<\/td><td>Client stops sending<\/td><td>cR=1, no server-side errors<\/td><\/tr><tr><td>timeout http-request<\/td><td>Partial headers<\/td><td>400\/408, sometimes 503 upstream<\/td><\/tr><tr><td>timeout http-keep-alive<\/td><td>Idle idle keep-alive socket<\/td><td>No error; pool churn instead<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">timeout connect: the backend is not answering SYNs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A 503 caused by <code>timeout connect<\/code> means the backend box never completed a TCP handshake within the window. It is not slow application code; it is a host that is down, firewalled, or accepting connections slower than the listen backlog allows. In <code>show stat<\/code>, the <code>sC<\/code> column increments per attempt, and a healthy backend shows zero on that counter for the whole minute.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ten seconds is a sane default for a LAN backend; two seconds is fine inside a single host. Raising it to 30 to &#8220;fix&#8221; 503s only stretches every failure into a longer user-visible stall, because failed health checks and retries all inherit the same window. If half your connects are slow, the problem is the backend&#8217;s SYN queue, not your HAProxy value.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">timeout server: the request is in flight and nothing comes back<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the most common misattribution. The connection succeeded, the request went out, and the backend stayed silent past the deadline, so HAProxy cuts the connection and returns 503 to the client. The distinguishing signal is <code>sH=1<\/code> with <code>sC=0<\/code> in the stats row: the server was reached but produced no response headers in time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Set this above your slowest legitimate response, not above your slowest observed one. If your p99 is 1.2s, a 60s value means a stuck backend consumes a connection slot for a minute before the client learns anything. Log <code>%ts<\/code> with a high precision to compare server response times to this timeout; if the 503 timestamps coincide with <code>%ts<\/code> near the limit, this directive is the culprit.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">timeout client: the client went quiet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>timeout client<\/code> applies in both directions: while waiting for the client&#8217;s request headers and while streaming the response back. A slow mobile client on a bad link can trip this and the connection is closed normally, but if the backend is also slow the pooled connection can be freed in a state that produces an upstream 503 on retry. Look at <code>cR<\/code> for client-side aborts and cross-check against your access log&#8217;s total request duration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Thirty seconds is generous for header arrival and tight for a large upload. Keep the two purposes separate: use <code>timeout http-request<\/code> for header deadlines and leave <code>timeout client<\/code> for the streaming phase. Conflating them is how people end up with 60-second uploads timing out at the 30-second client limit and blaming the app server.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">timeout http-request and keep-alive: the two that look harmless<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>timeout http-request<\/code> limits how long a client may take to send a complete header block. A default of five seconds works until a client sends a POST with a large header set over a slow link and gets a 408 instead. That 408 is not a 503, but it burns a frontend connection count, and with <code>maxconn<\/code> near its limit the next request genuinely gets 503 with no backend error at all.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>defaults\n  mode http\n  timeout connect 3s\n  timeout client  30s\n  timeout server  30s\n  timeout http-request 10s\n  timeout http-keep-alive 5s\n  timeout check 2s\n\nfrontend web\n  maxconn 2000\n  bind *:80\n  default_backend app<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>timeout http-keep-alive<\/code> never causes a 503 directly. It closes idle keep-alive sockets, and if it is shorter than the client&#8217;s own keep-alive plan, clients reconnect constantly and each reconnect consumes a frontend slot. On a VPS with <code>maxconn 2000<\/code> and <code>ulimit -n<\/code> still at 1024, that churn produces real 503s at the socket level. Check <code>ulimit -n<\/code> before touching any timer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ordering rules that prevent contradictions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Three invariants keep the ladder coherent. Keep-alive must be shorter than client, so an idle socket is reclaimed before the client deadline fires. Connect must be much shorter than server, so a dead host fails fast instead of sitting in a retry loop. And the health check timeout must be shorter than server, otherwise a single sick backend holds a check slot while the pool marks it up.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>timeout http-keep-alive<\/code> &lt; <code>timeout client<\/code> (typically 5s vs 30s)<\/li><li><code>timeout connect<\/code> &lt; <code>timeout server<\/code> (3s vs 30s)<\/li><li><code>timeout check<\/code> &lt; <code>timeout server<\/code> (2s vs 30s)<\/li><li><code>timeout http-request<\/code> close to <code>timeout connect<\/code>, never above client<\/li><li><code>maxconn<\/code> under 80 percent of the process fd limit<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Diagnosing with stats instead of guessing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Enable the stats socket and query it directly. The four counters that matter are <code>ereq<\/code> (request errors), <code>econ<\/code> (connection errors), <code>sC<\/code> (server connect retries) and <code>sH<\/code> (server aborts). A row where <code>econ<\/code> climbs while <code>sH<\/code> stays flat points at connect; the reverse points at server. That distinction, made in one command, saves a tuning round trip.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"show stat\" | socat stdio \/var\/run\/haproxy\/admin.sock \\\n  | cut -d, -f2,5,13,14,60,61\nhaproxy -c -f \/etc\/haproxy\/haproxy.cfg<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Make one change at a time and reload rather than restart; a reload keeps the stats counters, a restart resets them and wipes the evidence. After a fix, watch the 503 rate for a full keep-alive cycle, meaning at least twice the longest timeout you touched. On a well-sized <a href=\"https:\/\/virtualserversvps.com\/\">KVM virtual machine<\/a> the rate should fall to the background level within five minutes and stay there.<\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":14,"footnotes":""},"categories":[3],"tags":[],"class_list":["post-406","post","type-post","status-publish","format-standard","hentry","category-performance-optimization"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.1 (Yoast SEO v26.1) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s - Virtual Servers VPS Blog<\/title>\n<meta name=\"description\" content=\"HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to one\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s\" \/>\n<meta property=\"og:description\" content=\"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s\" \/>\n<meta property=\"og:url\" content=\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/\" \/>\n<meta property=\"og:site_name\" content=\"Virtual Servers VPS Blog\" \/>\n<meta property=\"article:published_time\" content=\"2026-06-12T03:59:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-03T22:04:07+00:00\" \/>\n<meta name=\"author\" content=\"Virtual-Servers-Vps-Editor\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Virtual-Servers-Vps-Editor\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/\",\"name\":\"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s - Virtual Servers VPS Blog\",\"isPartOf\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\"},\"datePublished\":\"2026-06-12T03:59:00+00:00\",\"dateModified\":\"2026-11-03T22:04:07+00:00\",\"author\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\"},\"description\":\"HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to one\",\"breadcrumb\":{\"@id\":\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/virtualserversvps.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#website\",\"url\":\"https:\/\/virtualserversvps.com\/blog\/\",\"name\":\"Virtual Servers VPS Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0\",\"name\":\"Virtual-Servers-Vps-Editor\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g\",\"caption\":\"Virtual-Servers-Vps-Editor\"},\"sameAs\":[\"https:\/\/virtualserversvps.com\/blog\"],\"url\":\"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s - Virtual Servers VPS Blog","description":"HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to one","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/","og_locale":"en_US","og_type":"article","og_title":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s","og_description":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s","og_url":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/","og_site_name":"Virtual Servers VPS Blog","article_published_time":"2026-06-12T03:59:00+00:00","article_modified_time":"2026-11-03T22:04:07+00:00","author":"Virtual-Servers-Vps-Editor","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Virtual-Servers-Vps-Editor","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/","url":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/","name":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s - Virtual Servers VPS Blog","isPartOf":{"@id":"https:\/\/virtualserversvps.com\/blog\/#website"},"datePublished":"2026-06-12T03:59:00+00:00","dateModified":"2026-11-03T22:04:07+00:00","author":{"@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0"},"description":"HAProxy returns 503 when it has a request in hand and no backend it is willing to use. The misleading part is that the full 503 rate rarely maps to one","breadcrumb":{"@id":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/virtualserversvps.com\/blog\/haproxy-timeout-ladder-tcp-http-establish\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/virtualserversvps.com\/blog\/"},{"@type":"ListItem","position":2,"name":"HAProxy Timeout Ladder: Which Timeout Is Causing Your 503s"}]},{"@type":"WebSite","@id":"https:\/\/virtualserversvps.com\/blog\/#website","url":"https:\/\/virtualserversvps.com\/blog\/","name":"Virtual Servers VPS Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/virtualserversvps.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/82a299a8284a66ff49f97c74684724a0","name":"Virtual-Servers-Vps-Editor","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/virtualserversvps.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/d820b15f1cd028e97610d9adf536df7be5cb6423869967037d468d5355fa003f?s=96&d=mm&r=g","caption":"Virtual-Servers-Vps-Editor"},"sameAs":["https:\/\/virtualserversvps.com\/blog"],"url":"https:\/\/virtualserversvps.com\/blog\/author\/virtualserversvps\/"}]}},"_links":{"self":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/406","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/comments?post=406"}],"version-history":[{"count":4,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/406\/revisions"}],"predecessor-version":[{"id":1595,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/posts\/406\/revisions\/1595"}],"wp:attachment":[{"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/media?parent=406"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/categories?post=406"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/virtualserversvps.com\/blog\/wp-json\/wp\/v2\/tags?post=406"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}