Keeping a TCP connection open across multiple requests is one of the oldest performance optimizations in HTTP, and it is also a standing liability. HTTP/1.1 keep-alive, formalized in RFC 7230/7231, lets a client reuse one connection for many requests instead of paying a new TCP and TLS handshake each time; HTTP/2, standardized in RFC 7540, goes further and multiplexes many logical streams over a single long-lived connection. Both designs trade a fixed, well-understood cost — handshake latency — for an open-ended one: server memory, file descriptors, and worker threads or processes that stay allocated for as long as the connection lives. That tradeoff has produced two of the most durable denial-of-service techniques in web history. Slowloris, tracked as CVE-2007-6750, kept thread-per-connection servers like Apache HTTP Server pinned for over a decade with nothing more than a trickle of partial headers. Sixteen years later, on October 10, 2023, the HTTP/2 Rapid Reset technique (CVE-2023-44487) used the same underlying idea — hold work open, cheaply, at scale — to drive some of the largest Layer 7 DDoS attacks ever recorded, with Google alone reporting a peak of 398 million requests per second. This piece walks through why connection lifetime is a security variable, not just a performance knob, and what the fixes actually changed.
Why does keeping a connection open cost the server more than the client?
Keeping a connection open is asymmetric because the client only holds a socket, while the server typically holds a socket plus whatever execution context serves it. In a thread-per-connection or process-per-connection model — the design Apache HTTP Server used through its 1.x and 2.x lines — every open connection ties up a worker from a finite pool, regardless of whether that connection is actively sending data. A client can open a connection, send a few bytes, and go silent; the server still has to keep the worker, its stack, and any buffered state allocated until a timeout fires or the connection closes. Event-driven servers like nginx handle many idle connections per worker process instead of one, which is why they were largely unaffected by Slowloris by design rather than by patching. The general lesson holds regardless of architecture: any resource allocated "per open connection" — a thread, a file descriptor, a TLS session buffer, a stream table entry — is a pool an attacker can try to drain simply by opening connections faster than they close, or holding them open longer than intended.
What made Slowloris work, and how was it fixed?
Slowloris, disclosed as CVE-2007-6750, worked by opening many connections to a target and sending legitimate-looking HTTP headers one at a time, at intervals just short of the server's timeout, so each request never completed. Because Apache's threaded and prefork MPMs dedicated a worker to each connection for the duration of the request, and had no default limit on how long an incomplete request could take, a modest number of slow connections — often cited in the hundreds, not thousands — could exhaust the entire worker pool on a default configuration, blocking legitimate traffic on commodity hardware and bandwidth. The fix landed as mod_reqtimeout, shipped in Apache HTTP Server 2.2.15 in 2010, which lets administrators cap how long a client has to send the request line, headers, and body before the connection is dropped. Nginx and other event-driven servers were structurally resistant because idle, header-incomplete connections don't consume a dedicated worker. Per NVD's CVE-2007-6750 record and Red Hat's advisory guidance, mod_reqtimeout (or an equivalent read-timeout directive) remains the standard mitigation today.
How did HTTP/2 Rapid Reset turn a protocol feature into an attack?
HTTP/2 Rapid Reset (CVE-2023-44487), publicly disclosed on October 10, 2023, targets the stream-cancellation mechanism inside HTTP/2's multiplexing model rather than connection idling. A single HTTP/2 connection can carry many concurrent streams, each representing one request-response pair, and the client is allowed to cancel a stream at any time by sending an RST_STREAM frame. The attack opens a stream and immediately resets it, then repeats this thousands of times per second over the same connection — the server still has to allocate request-handling state and dispatch work to the backend for each stream before the reset is processed, but because canceled streams don't count toward the server's max_concurrent_streams limit the same way an open stream does, the attacker evades the cap designed to bound concurrent work per connection. Google, Cloudflare, and Amazon Web Services jointly disclosed the technique and reported record-setting attacks in August–October 2023: Google measured a peak of 398 million requests per second, Cloudflare 201 million, and AWS 155 million, according to each vendor's public writeup and CISA's coordinated advisory. Fixes shipped across nginx, Envoy, gRPC, and other HTTP/2 implementations as stream-creation rate limits and stricter accounting for reset streams.
What connection-level settings actually bound this risk in practice?
The practical mitigations are the same handful of timeout and rate-limiting knobs, whether the risk is a slow trickle of headers or a burst of canceled streams. Nginx's documented defaults illustrate the shape of the problem: keepalive_timeout defaults to 75 seconds (how long an idle keep-alive connection is held open), keepalive_requests defaults to 1000 (how many requests one connection may serve before nginx forces a new one), and http2_max_concurrent_streams defaults to 128 (the cap Rapid Reset was designed to sidestep). Since nginx 1.19.7, HTTP/2 connection and request limits were folded into the same HTTP/1.1-style directives rather than kept as a separate config surface, which simplifies auditing both protocols together. Beyond server defaults, the well-established mitigation set includes per-IP concurrent-connection caps, minimum-bytes-per-second read thresholds so a connection sending data too slowly gets dropped, and terminating connections at a reverse proxy or L7 load balancer that is purpose-built to absorb this class of abuse before it reaches application servers — separating the connection-handling problem from the application logic entirely.
Is this the same problem as a dependency or supply-chain vulnerability?
No — connection-level exhaustion is a transport and protocol-handling risk, while supply-chain security is about what code and packages actually run in your application, and the two call for different controls. Slowloris and Rapid Reset are exploitable regardless of how clean your application code or dependency tree is, because the vulnerable surface is the web server's or load balancer's connection-handling layer, not a library you imported. That said, the two domains do intersect operationally: the same web servers, proxies, and gRPC libraries patched against Rapid Reset in late 2023 were themselves dependencies with version numbers, and organizations tracking their exposure needed to know precisely which builds of nginx, Envoy, or a given HTTP/2 stack they had deployed. That's a software composition and inventory question — knowing what's running and whether it's patched — even when the vulnerability class itself is a network-layer one rather than an application-logic one. Treating them as the same problem, or expecting an SCA or SBOM tool to catch a protocol-handling flaw like Rapid Reset on its own, misses the fact that connection-layer hardening lives in server and proxy configuration, not in a dependency manifest.