What Is a HTTP Slow Read Attack? | TCP Receive Window Exhaustion

Learn how the HTTP Slow Read attack manipulates the TCP receive window to force servers into tiny response fragments, exhausting send buffers. Covers the mechanism, how it differs from Slowloris, and mitigation.

HTTP Slow Read attack (also called Slow Read DoS) is a low-and-slow denial-of-service technique in which the client sends a complete, valid HTTP request but then advertises an artificially small TCP receive window, forcing the server to transmit its response one tiny fragment at a time and hold the connection — and its send buffer — open for an extended period. Unlike attacks that stall a request, Slow Read stalls the response, exhausting server-side send buffers and connection slots rather than CPU or bandwidth.

TL;DR: After a normal request completes, the attacker sets its advertised TCP receive window to a near-zero value (as low as 1 byte), which forces the server’s TCP stack to obey flow control and send data in minuscule chunks, sending periodic TCP Window Probes to check if the window has opened. The server’s send buffer stays full and the connection stays open far longer than a normal response would take, and doing this across many parallel connections exhausts available send buffers, connection slots, or worker threads. Mitigation requires response-side timeouts (send_timeout), minimum-throughput enforcement, TCP-level window floors, and terminating connections at an edge or reverse proxy layer that can absorb slow response delivery without tying up the origin.

Last updated: 2026-08-27

Context: a response-side variant of low-and-slow

Slow Read attacks emerged as security researchers and pentest tool authors (slowhttptest is the most widely used public implementation) extended the low-and-slow attack family beyond request-side techniques like Slowloris and R.U.D.Y. Where those attacks exploit ambiguity in when an HTTP request is considered complete, Slow Read exploits a transport-layer mechanism — TCP flow control — that has nothing to do with HTTP request framing at all. It works against any HTTP server sitting on top of a standard TCP stack, because the vulnerability is a normal, specification-compliant behavior of TCP itself, not a bug.

This article assumes familiarity with the general low-and-slow attack category and focuses specifically on the receive-window mechanism. For request-side low-and-slow techniques, see What Is a Slowloris Attack?.

How the TCP receive window normally works

Every TCP segment carries a Window Size field that tells the sender how many bytes of unacknowledged data the receiver is currently willing to accept into its receive buffer. This is TCP’s built-in flow-control mechanism, defined in RFC 793 and refined by window scaling (RFC 7323): it exists to prevent a fast sender from overrunning a slow receiver’s buffer.

Normal exchange:
Client → Server: HTTP GET request (complete)
Server internally: prepares a 500 KB response
Client's TCP stack: advertises Window Size = 65,535 (or larger with window scaling)
Server → Client: sends response in large segments, limited only by MSS and congestion control
Client ACKs promptly, window stays open
Connection completes in milliseconds to low seconds

A client with a healthy, high-bandwidth connection advertises a large window (tens of kilobytes, or megabytes with window scaling enabled) and the transfer proceeds at near-line-rate.

How the attack manipulates the window

Slow Read attacks abuse the fact that a receiver is fully entitled, under the TCP specification, to advertise an arbitrarily small window — there is no minimum enforced by the protocol.

Slow Read attack:
Step 1: Attacker sends a complete, syntactically valid HTTP request
Step 2: Server begins preparing the response (e.g., a 500 KB page or file)
Step 3: Attacker's TCP stack advertises Window Size = 1 (or another near-zero value)
instead of the normal 65,535+
Step 4: Server's TCP stack, honoring flow control, can send at most 1 byte
before it must stop and wait for an ACK that opens the window further
Server → Client: 1 byte sent
Client → Server: ACK, Window Size = 1 (still near-zero)
Server → Client: 1 more byte sent
[repeats for the entire response length]
Step 5: Server's send buffer — typically 64 KB-128 KB — fills because data
cannot leave the buffer as fast as the application writes to it
Step 6: Server begins sending TCP Window Probes (also called zero-window
probes) — small packets that ask "has your window opened yet?"
Each probe consumes CPU cycles and keeps kernel connection state active
Step 7: Repeated across dozens to hundreds of parallel connections, the
server's pool of send buffers, connection slots, or worker
threads/processes is exhausted

The attacker can additionally combine a small window with a slow ACK cadence, further stretching out how long each connection occupies server resources. Because every packet exchanged is a fully valid TCP segment, there is nothing for a signature-based packet filter to flag.

What makes Slow Read distinct: send-buffer exhaustion, not receive-side stalling

The critical technical distinction between Slow Read and request-stalling attacks like Slowloris is which side of the connection is being starved:

  • In Slowloris, the server is waiting to receive data (the rest of the request headers) that never arrives. The exhausted resource is typically a worker thread/process waiting in a read state.
  • In Slow Read, the server has already received the full request and is trying to send a response, but the client’s advertised window prevents the data from leaving the server’s send buffer at any reasonable rate. The exhausted resource is the send buffer itself, plus whatever connection slot or thread remains allocated while the send is in progress.

This means Slow Read is effective even against servers whose request-side timeouts (RequestReadTimeout, client_header_timeout) are already hardened against Slowloris — those settings govern how long the server waits to receive data, not how long it’s allowed to spend sending a response. A server hardened only against request-side low-and-slow attacks can remain fully exposed to Slow Read unless response-side timeouts are separately configured.

Slowloris vs. HTTP Slow Read vs. RUDY

CharacteristicSlowlorisHTTP Slow ReadR.U.D.Y.
HTTP phase targetedRequest headersResponse body deliveryRequest body (POST)
MechanismNever sends CRLF CRLFNear-zero TCP receive window1 byte of POST body per interval
Exhausted resourceWorker thread waiting to receiveSend buffer + connection slotWorker thread waiting to receive body
Requires a response to existNo — request never completesYes — server must have data to sendNo — request never completes
TCP-layer manipulationNoneCentral (window size field)None
Effective against static file servingLimited (no headers to withhold on GET without body)Yes — any response, including static assetsNo — needs a POST endpoint
Detectable via RequestReadTimeout/header timeout aloneYesNo — operates after headers/body are receivedPartially
Typical toolslowloris.pl and derivativesslowhttptest -g -o slow_read_stats.csv -Xr-u-dead-yet, OWASP DOS HTTP POST

Slow Read is arguably broader in scope than Slowloris because it can target any endpoint that returns a non-trivial response body — including static assets like images or JS bundles — whereas Slowloris and R.U.D.Y. depend on specific request phases (headers, or a POST body) that not every request even has.

Server-side exposure notes

Server / concernExposureWhy
Servers with no send_timeout/response-timeout configuredHighNothing bounds how long a slow send can occupy a connection
Thread/process-per-connection servers (Apache Prefork/Worker, older IIS)HighEach slow send pins a full worker for its duration
Async/event-driven servers (nginx, LiteSpeed)MediumDoesn’t block a thread, but send buffers and connection tables are still finite
Servers serving large static files or uncompressed responsesHigherMore response bytes means more time an attacker can stretch out the send
Servers behind a buffering reverse proxy or edge networkLowThe proxy/edge receives the full response quickly from origin and independently paces delivery to the slow client, so the origin’s connection is freed almost immediately

Detection signals and telemetry

Like other low-and-slow techniques, Slow Read produces almost no volumetric signal, but it leaves specific traces at the TCP and connection-state level.

Terminal window
# Look for connections stuck in a "sending" state for unusually long
ss -o state established '( dport = :80 or dport = :443 )' -i | grep -i "send"
# Capture and inspect advertised window sizes — a sustained near-zero window
# is the direct signature of this attack
tcpdump -i eth0 'tcp port 443' -nn -v | grep -i "win 0\|win 1\b"
# Check for TCP Window Probe / zero-window activity in kernel counters
nstat -az | grep -i "zerowindow\|probe"
# nginx: connections lingering in "writing" state longer than expected
nginx -T | grep send_timeout
IndicatorNormalUnder Slow Read
Advertised client TCP windowTens of KB or more (with scaling)Near zero (1–64 bytes), sustained
TCP Window Probe / zero-window packetsRare, transientFrequent and sustained per connection
Connection duration for a given response sizeProportional to size and normal bandwidthOrders of magnitude longer than expected
Server send-buffer utilizationFluctuates with loadSaturated, not draining
Bandwidth used by the attacking clientN/ANear zero
CPU from probe/retransmit processingBaselineElevated relative to traffic volume

Mitigation techniques

1. Enforce response-side timeouts, not just request-side ones

Hardening RequestReadTimeout or client_header_timeout alone does nothing against Slow Read, since the request has already been fully received. A separate timeout must bound how long a response is allowed to take to send.

# nginx — bounds time between successive writes to the client
send_timeout 10s;
# Apache — mod_reqtimeout also supports a write-phase-adjacent bound
# via keepalive and standard Timeout directives; pair with an
# upstream/reverse proxy for stronger response-side enforcement
Timeout 20

2. Enforce a minimum effective throughput, not just a timeout

A fixed timeout can still allow an attacker to trickle just enough data to avoid triggering it. Where supported, enforce a minimum bytes/sec rate for outbound data, mirroring the MinRate approach used against Slowloris on the request side.

3. Set a floor on the accepted TCP window, where the platform allows it

Some load balancers and reverse proxies allow configuring a minimum window size they will honor from a client, effectively refusing to let a peer stall a connection below a certain throughput floor. This is less commonly exposed than application-layer timeouts, but where available it directly neutralizes the mechanism.

4. Limit concurrent slow-sending connections per IP

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn perip 20;

Capping concurrent connections per source limits how many send buffers a single attacker can occupy simultaneously, regardless of how long each one is held.

5. Terminate and buffer responses at a reverse proxy or edge layer

The most structurally effective mitigation separates the origin’s response generation from the slow delivery to the client. A reverse proxy or edge node receives the full response from the origin at normal speed, frees the origin’s connection almost immediately, and then independently paces delivery to the slow client using its own connection and buffer pool — one designed to absorb many concurrent slow deliveries without tying up backend capacity.

6. Serve large or expensive responses through a CDN/cache layer

Responses served from a cache at the edge never touch the origin’s send buffer for a given request at all, which removes the origin from the attack surface for cached content entirely.

Common mistakes

MistakeWhy it failsBetter approach
Assuming Slowloris mitigations (RequestReadTimeout, client_header_timeout) also stop Slow ReadThose bound request reception, not response deliveryConfigure send_timeout / response-phase timeouts separately
Only monitoring inbound traffic volumeSlow Read’s signature is in the outbound TCP window field, not inbound volumeInspect advertised window sizes and zero-window probe counters
Treating a small TCP window as inherently maliciousReal clients on constrained or lossy networks can legitimately advertise small windows brieflyCombine window-size signals with connection duration and per-IP concurrency before blocking
Serving large uncompressed responses without a buffering layer in frontLarger responses give the attacker more time to stretch the exploit window per connectionCompress responses, cache what can be cached, and buffer delivery at a proxy/edge layer
Ignoring response-side connections in capacity planningSend-buffer and connection-slot exhaustion can occur even with request-side defenses fully hardenedInclude response-in-flight connections in load and capacity testing

How to Implement on Azion

Azion sits between the client and the origin, which changes who actually absorbs a slow-window response delivery.

  • Firewall can apply per-IP concurrent connection limits that reduce how many slow-read sessions a single source can hold open at once
  • WAF can help apply behavioral rules against connection patterns consistent with slow response abuse, depending on configured rule sets
  • DDoS Protection provides always-on detection aimed at Layer 7 connection-exhaustion patterns, including low-and-slow techniques
  • Cache can serve eligible responses directly from the edge, which removes the origin’s send buffer from the attack surface for cached content
  • WAAP combines these controls for teams that want WAF, DDoS protection, and related defenses layered together

Because the edge receives a response from origin quickly and then independently paces delivery to a slow client, an attacker manipulating the TCP receive window generally ties up edge-side capacity — built for high connection concurrency — rather than origin resources.

Frequently Asked Questions

What is an HTTP Slow Read attack? An HTTP Slow Read attack sends a complete, valid HTTP request and then advertises an artificially small TCP receive window, forcing the server to send its response in tiny fragments and hold the connection — and its send buffer — open far longer than a normal transfer would take. It exhausts send buffers and connection slots rather than bandwidth or CPU.

What is the TCP receive window and why does the attack manipulate it? The TCP receive window is a field in every TCP segment that tells the sender how many bytes of unacknowledged data the receiver is currently willing to accept. TCP’s flow-control design requires the sender to honor it, so a receiver advertising a near-zero window forces the sender — the server — to transmit data one tiny fragment at a time, which is exactly the leverage the attack uses.

How is HTTP Slow Read different from Slowloris? Slowloris stalls the request by never sending the header-terminating CRLF CRLF sequence, so the server is left waiting to receive data. Slow Read lets the request complete normally and instead stalls the response by manipulating the TCP receive window, so the server is left unable to send data it has already prepared. They target opposite halves of the request/response cycle and require different mitigation.

Does hardening Slowloris defenses also stop Slow Read? No. Request-side timeouts like Apache’s RequestReadTimeout or nginx’s client_header_timeout bound how long the server waits to receive a request, which has no effect once a request has already completed and the server has moved on to sending a response. Slow Read requires separate response-side timeouts, such as send_timeout.

What is a TCP Window Probe? A TCP Window Probe (also called a zero-window probe) is a small packet a sender transmits periodically to check whether a receiver’s previously near-zero advertised window has opened up enough to resume sending data. Frequent, sustained window probes on many connections are a strong signal of an ongoing Slow Read attack, since legitimate zero-window conditions are normally brief.

Can Slow Read target static content like images or JavaScript files? Yes. Because Slow Read only requires that the server have some response body to send, it can target any endpoint returning a non-trivial payload, including static assets. This makes it broader in applicable scope than Slowloris or R.U.D.Y., which depend on specific request phases such as headers or a POST body.

Does compressing responses help against Slow Read? It can help somewhat by reducing the total bytes the server must push through a throttled window, which shortens how long each connection can be stretched out for a given piece of content. It does not eliminate the attack, since even a small compressed response can still be sent one byte at a time if the window stays near zero.

Is a small advertised TCP window always evidence of an attack? No. Clients on genuinely constrained, congested, or high-loss networks can legitimately advertise a small window for brief periods as part of normal TCP flow control. The distinguishing signal is a sustained near-zero window paired with an unusually long-lived connection and, often, many similar connections concentrated on a few source IPs.

Does using a CDN or edge cache reduce exposure to Slow Read? Yes, for cacheable content. If a response can be served directly from an edge cache, the origin server never has to hold a send buffer open for that request at all — the slow delivery, if any, happens between the edge and the client, using edge-side capacity built to handle many concurrent slow connections.

What tool is commonly used to test or demonstrate Slow Read? slowhttptest is the most widely used open-source tool for testing low-and-slow techniques, including Slow Read via its -g -o slow_read_stats.csv -X mode, which simulates clients advertising a small receive window against a target server for authorized testing purposes.

Can a reverse proxy fully neutralize Slow Read on the origin? A properly configured buffering reverse proxy substantially reduces exposure: it can receive the full response from the origin quickly, free the origin’s connection, and then independently pace slow delivery to the client using its own resources. This does not eliminate the attack in the abstract, but it moves the resource cost from the origin to the proxy/edge layer, which is typically built with much higher connection concurrency headroom.

Sources

  • IETF. “Transmission Control Protocol.” RFC 793.
  • IETF. “TCP Extensions for High Performance.” RFC 7323 (window scaling).
  • IETF. “HTTP Semantics.” RFC 9110.
  • OWASP. “Denial of Service Cheat Sheet.”
  • NIST. “Guide to Intrusion Detection and Prevention Systems.” SP 800-94.
  • nginx. “Module ngx_http_core_module: send_timeout.” Official Documentation.
stay up to date

Subscribe to our Newsletter

Get the latest product updates, event highlights, and tech industry insights delivered to your inbox.