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 responseClient'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 controlClient ACKs promptly, window stays openConnection completes in milliseconds to low secondsA 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 requestStep 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 furtherServer → Client: 1 byte sentClient → 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 itStep 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 activeStep 7: Repeated across dozens to hundreds of parallel connections, the server's pool of send buffers, connection slots, or worker threads/processes is exhaustedThe 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
| Characteristic | Slowloris | HTTP Slow Read | R.U.D.Y. |
|---|---|---|---|
| HTTP phase targeted | Request headers | Response body delivery | Request body (POST) |
| Mechanism | Never sends CRLF CRLF | Near-zero TCP receive window | 1 byte of POST body per interval |
| Exhausted resource | Worker thread waiting to receive | Send buffer + connection slot | Worker thread waiting to receive body |
| Requires a response to exist | No — request never completes | Yes — server must have data to send | No — request never completes |
| TCP-layer manipulation | None | Central (window size field) | None |
| Effective against static file serving | Limited (no headers to withhold on GET without body) | Yes — any response, including static assets | No — needs a POST endpoint |
Detectable via RequestReadTimeout/header timeout alone | Yes | No — operates after headers/body are received | Partially |
| Typical tool | slowloris.pl and derivatives | slowhttptest -g -o slow_read_stats.csv -X | r-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 / concern | Exposure | Why |
|---|---|---|
Servers with no send_timeout/response-timeout configured | High | Nothing bounds how long a slow send can occupy a connection |
| Thread/process-per-connection servers (Apache Prefork/Worker, older IIS) | High | Each slow send pins a full worker for its duration |
| Async/event-driven servers (nginx, LiteSpeed) | Medium | Doesn’t block a thread, but send buffers and connection tables are still finite |
| Servers serving large static files or uncompressed responses | Higher | More response bytes means more time an attacker can stretch out the send |
| Servers behind a buffering reverse proxy or edge network | Low | The 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.
# Look for connections stuck in a "sending" state for unusually longss -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 attacktcpdump -i eth0 'tcp port 443' -nn -v | grep -i "win 0\|win 1\b"
# Check for TCP Window Probe / zero-window activity in kernel countersnstat -az | grep -i "zerowindow\|probe"
# nginx: connections lingering in "writing" state longer than expectednginx -T | grep send_timeout| Indicator | Normal | Under Slow Read |
|---|---|---|
| Advertised client TCP window | Tens of KB or more (with scaling) | Near zero (1–64 bytes), sustained |
| TCP Window Probe / zero-window packets | Rare, transient | Frequent and sustained per connection |
| Connection duration for a given response size | Proportional to size and normal bandwidth | Orders of magnitude longer than expected |
| Server send-buffer utilization | Fluctuates with load | Saturated, not draining |
| Bandwidth used by the attacking client | N/A | Near zero |
| CPU from probe/retransmit processing | Baseline | Elevated 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 clientsend_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 enforcementTimeout 202. 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
| Mistake | Why it fails | Better approach |
|---|---|---|
Assuming Slowloris mitigations (RequestReadTimeout, client_header_timeout) also stop Slow Read | Those bound request reception, not response delivery | Configure send_timeout / response-phase timeouts separately |
| Only monitoring inbound traffic volume | Slow Read’s signature is in the outbound TCP window field, not inbound volume | Inspect advertised window sizes and zero-window probe counters |
| Treating a small TCP window as inherently malicious | Real clients on constrained or lossy networks can legitimately advertise small windows briefly | Combine window-size signals with connection duration and per-IP concurrency before blocking |
| Serving large uncompressed responses without a buffering layer in front | Larger responses give the attacker more time to stretch the exploit window per connection | Compress responses, cache what can be cached, and buffer delivery at a proxy/edge layer |
| Ignoring response-side connections in capacity planning | Send-buffer and connection-slot exhaustion can occur even with request-side defenses fully hardened | Include 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.
Related Resources
- Low and Slow Attacks
- What Is a Slowloris Attack?
- What Is a DDoS Attack?
- What Is SYN Flood?
- Azion WAF
- Azion DDoS Protection
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.