Once an HAProxy stick-table counter marks a client as abusive, you can respond with `http-request deny`, `http-request tarpit` or `http-request silent-drop`. What does each do to the client and to your own resources?
answer
- immediate answer versus held connection
- tarpit occupies a session slot
- silent-drop sends nothing back
- middleboxes keep the flow alive
- clear 429 for real clients
basics
~20 sdeny answers immediately with an error and frees the connection, tarpit holds the request for the tarpit timeout before answering and ties up a session slot meanwhile, and silent-drop discards the connection without telling the client, costing HAProxy least but leaving state on every device in between.
solid answer
~50 s`http-request deny` returns an error at once — 403 unless you set `deny_status` — which is cheap for HAProxy and clear for the client, so it is the right answer for ordinary throttling where a caller needs a 429 it can back off from. `http-request tarpit` accepts the request and holds it for `timeout tarpit`, which defaults to `timeout connect`, before returning an error; that slows a scraper down but keeps a session and its file descriptor occupied for the whole delay, so a determined attacker can turn it into a resource-exhaustion vector against you. `http-request silent-drop` closes the connection without sending anything back, suppressing the reset so the client sits waiting for its own timeout; it costs HAProxy almost nothing, which is why it survives much heavier abuse than tarpit. Its catch is that every stateful device between you and the client — firewalls, connection-tracking tables, cloud load balancers — keeps that flow alive, so you can exhaust their state instead of your own.
go deeper
Know the three shapes of answer: reply with an error now, hold the request first, or say nothing at all. Recall that deny is the ordinary choice.
Explain that tarpit's delay comes from timeout tarpit, that it keeps a session occupied throughout, and that silent-drop returns nothing so the client waits out its own timeout.
Weigh the resource economics on both sides: tarpit spends your session table, silent-drop spends the state tables of every middlebox in the path, and deny gives an attacker precise feedback. Recommend a tiered policy and say what you would check before enabling silent-drop.
Own the abuse-response policy across the estate — which tier applies to which class of traffic, what the customer-visible contract is for a throttled caller, and where the blast radius of a silent drop lands on shared infrastructure you do not control.
## Three ways to say no The stick table gives you the verdict; these actions decide what the client experiences. They differ along two axes that pull in opposite directions: how much information the client gets, and how much it costs you to keep saying no. ``` frontend fe_public bind :80 stick-table type ip size 100k expire 10m store http_req_rate(10s) http-request track-sc0 src http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 } http-request silent-drop if { sc_http_req_rate(0) gt 1000 } ``` A tiered response like this is the usual shape: a clear 429 for clients that are merely enthusiastic, something harsher for traffic that is obviously not a client at all. ## deny Stops rule evaluation and returns an error response immediately. Default status is 403; `deny_status` sets something else, and 429 is what a rate-limited HTTP client should see so its own retry and backoff logic engages. The request never reaches a backend, so no application connection is consumed. The connection itself is released quickly. The trade is that an immediate, well-formed answer is also perfect feedback for whoever is probing you: they learn instantly that they were blocked and can retry, rotate, or tune their rate right up to your threshold. For legitimate clients that is a feature, and it is why deny is the default choice for anything with real users behind it. Making a scraper wait is not worth confusing your own customers. ## tarpit Accepts the request and deliberately does nothing with it for `timeout tarpit`, which falls back to `timeout connect` when not set, then returns an error — 500 by default, or whatever `deny_status` names. The intent is economic: make each abusive request cost the attacker a held connection rather than a fast rejection, so a scraper's throughput collapses. The cost lands on you too. A tarpitted request occupies an HAProxy session and its file descriptor for the whole delay. Multiply by a real flood and the tarpit becomes the attack: you fill your own session table holding connections open on purpose. Tarpit is a scalpel for a handful of misbehaving clients, not a shield against volume, and it needs to be paired with a sane `maxconn` so the held sessions cannot crowd out legitimate ones. ## silent-drop Makes the client-facing connection disappear without notifying the client. HAProxy suppresses the reset that would normally be sent — using the kernel's connection-repair facility where available, otherwise by emitting the reset with a time-to-live that expires before it can reach the client. The client sees an established connection that has simply gone quiet, and waits out its own timeout. That produces a tarpit-like delay for the attacker at almost no local cost, which is why it withstands far higher volumes than tarpit does. The documented caution is what makes this a senior question: every stateful device in the path also keeps believing the connection is alive. Your firewall's connection-tracking table, an upstream cloud load balancer, a NAT gateway — each holds an entry until its own idle timeout. Under a large flood you can exhaust that shared state and take out traffic that has nothing to do with the abusive client. Suppressing the reset may also require privileges the process does not have by default. ## Choosing A workable policy for most services: - **Real clients, ordinary overuse:** `deny` with `deny_status 429`. Anything else makes your API undebuggable for the customer whose retry loop is misconfigured. - **Clearly hostile, low volume:** tarpit, if you have session headroom and want the requests to cost something. - **Clearly hostile, high volume:** silent-drop, and only after checking what sits between HAProxy and the internet — if a stateful firewall or a managed load balancer is in the path, you are spending its state table, not yours. And whichever you choose, the highest-value step is usually to run the rule in observation mode first, counting what it would have hit before it starts hitting anything.
- Why can a tarpit rule become a self-inflicted denial of service?Each tarpitted request holds an HAProxy session and file descriptor for the duration of `timeout tarpit`. Under a flood you accumulate held sessions faster than they drain, filling your own connection budget and starving legitimate traffic. The mechanism costs the attacker a connection but costs you one too, which does not scale in your favour.
- What does silent-drop cost that does not show up in HAProxy's own metrics?State on every device between HAProxy and the client. Firewalls, connection-tracking tables, NAT gateways and managed load balancers all keep believing the connection is alive until their own idle timeouts fire. A large flood can exhaust that shared state and affect unrelated traffic, while HAProxy's own resource graphs look healthy.
- Which action fits an API whose callers are paying customers?`deny` with `deny_status 429`. A well-formed response is the only one a client library can react to, and a customer whose retry loop is misconfigured needs to see the throttle rather than a mysterious hang. Save the silent options for traffic that is clearly not a client of yours at all.
saying these in an interview costs you the question
- Treating tarpit as free because the attacker waits
- Believing silent-drop consumes no resources anywhere
- Sending silent drops to legitimate API clients
- Assuming deny returns 429 without deny_status
- Thinking tarpit hides that the client was blocked