What Is a Slowloris Attack? | CRLF CRLF Header Exhaustion Explained

Learn how Slowloris opens thousands of partial HTTP connections to exhaust a server's thread pool. Covers CRLF CRLF mechanics, its 2009 origin, per-server vulnerability, and detailed mitigation with RequestReadTimeout and limit_conn.

Slowloris is a low-and-slow denial-of-service technique that opens many HTTP connections to a web server and holds each one open indefinitely by sending partial request headers at intervals just under the server’s timeout, never transmitting the CRLF CRLF sequence that marks the end of an HTTP header block. It exhausts the server’s thread or connection pool using minimal bandwidth, and a single unhardened machine can take down a default Apache server in under two minutes.

TL;DR: Slowloris opens hundreds of TCP connections to a web server and sends each one an incomplete HTTP request — headers with no terminating CRLF CRLF — then trickles one extra header byte every 10–15 seconds to reset the server’s idle timeout without ever finishing the request. Thread-per-connection servers like Apache’s prefork MPM run out of worker threads within seconds to minutes because each stalled connection occupies a thread until the connection or read timeout fires. Mitigation combines aggressive per-phase timeouts (RequestReadTimeout, client_header_timeout), per-IP connection caps, event-driven or async server architectures, and edge-side request buffering that never forwards an incomplete request to the origin.

Last updated: 2026-08-27

Origin: Robert “RSnake” Hansen, 2009

Slowloris was released in June 2009 by security researcher Robert “RSnake” Hansen as a Perl script (slowloris.pl). Hansen had been documenting HTTP-layer denial-of-service techniques for years, but Slowloris was the first widely distributed tool to demonstrate that a single consumer-grade machine, with no botnet and negligible bandwidth, could take down a production web server by exploiting how HTTP/1.1 servers wait for header completion.

The technique gained public attention a year later when it was reportedly used during the 2009 Iranian election protests to disrupt government web infrastructure, and again in disputes involving activist and political sites, cementing its reputation as a low-cost, high-leverage denial-of-service tool. OWASP subsequently adopted Slowloris as a reference case in its Denial of Service Cheat Sheet and maintains a community page describing the technique and known countermeasures.

The vulnerability Slowloris exploits is not a bug in any specific web server — it is a direct consequence of how HTTP/1.1 (RFC 9112, formerly RFC 7230) defines request framing: a server has no way to know a request is “done” until it either receives the header-terminating sequence or the connection times out. Slowloris weaponizes that ambiguity.

How Slowloris works: the CRLF CRLF mechanism

HTTP/1.1 delimits the end of a request’s header block with two consecutive CRLF (\r\n) sequences — commonly written as CRLF CRLF or \r\n\r\n. A complete, well-formed request looks like this on the wire:

GET / HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: Mozilla/5.0\r\n
\r\n ← empty line = CRLF CRLF = "headers are done"

The server has no way to begin processing the request until it sees that final empty line. Slowloris abuses this by sending a request that never reaches it:

Step 1 — Open the connection and send a partial, valid request line + headers:
GET / HTTP/1.1\r\n
Host: target.com\r\n
User-Agent: Mozilla/5.0\r\n
X-a: b\r\n
← no terminating CRLF CRLF sent yet
Step 2 — Wait just under the server's read/idle timeout (commonly 10–15 seconds)
Step 3 — Send one more harmless partial header to reset the timeout clock:
X-a: c\r\n
Step 4 — Repeat step 2–3 indefinitely, for hundreds of connections in parallel

Each individual byte sent is a syntactically valid HTTP header line. There is no malformed packet, no protocol violation, and no signature a stateless packet filter can match — which is why Slowloris passes straight through devices that only inspect for malformed traffic.

Why this exhausts the server. Many web servers allocate a worker thread, process, or a slot in a fixed-size connection table the moment a TCP connection is accepted and an HTTP request begins arriving. That resource stays reserved until the request completes, errors, or times out. With enough parallel half-sent requests, every available worker is pinned waiting for a CRLF CRLF that never comes. New, legitimate connections then queue behind a full connection pool and eventually receive a connection refusal or an HTTP 408 Request Timeout.

Attack timeline against a default Apache install (MaxRequestWorkers ≈ 150–256):
t=0s Attacker opens 300 sockets, sends partial headers on each
t=1-10s Sockets fill Apache's worker pool (150-256 workers occupied)
t=10s+ Legitimate requests queue; new connections refused or time out
t=10-15s Attacker sends 1 keep-alive byte per stalled socket, resets timers
t=∞ Cycle repeats — server remains saturated until attack stops

Slowloris vs. HTTP Flood vs. HTTP Slow Read

Slowloris is frequently confused with other Layer 7 attacks that also target application resources rather than bandwidth. The table below isolates what distinguishes it.

CharacteristicSlowlorisHTTP FloodHTTP Slow Read
HTTP phase exploitedRequest headers (never completed)None — requests are complete and validResponse delivery (client reads slowly)
Resource exhaustedWorker threads / connection slotsCPU, database, application logicServer send buffer + connection slots
Request volume1 request per connection, incompleteVery high — thousands of complete req/s1 complete request per connection
Bandwidth signatureNear-zero, flatHigh and often burstyNear-zero, flat
TCP window manipulationNot usedNot usedCentral to the mechanism
Detectable by rate limitingNo — no request rate to limitYes — request/sec limiting worksNo — no request rate to limit
Connections needed to succeedHundreds (thread-per-connection servers)Thousands (varies by app cost)Hundreds
Typical origin defenseTimeouts, limit_conn, async serverWAF rate limiting, CAPTCHA, rulesSend-buffer limits, response timeouts

Slowloris and HTTP Slow Read are siblings within the broader low-and-slow attack category: both hold connections open with minimal data and minimal bandwidth, but Slowloris stalls the request side of the exchange while Slow Read stalls the response side by manipulating the TCP receive window. See What Is a HTTP Slow Read Attack? for the receive-window mechanism in detail.

Vulnerability by server software

Slowloris’s real-world impact depends almost entirely on whether the target server dedicates a thread or process to each open connection, or whether it multiplexes many connections through an event loop.

Server / concurrency modelDefault connection handlingSlowloris exposureNotes
Apache MPM Prefork1 process per connectionHighMaxRequestWorkers (default 150–256) exhausts fast; historically the reference vulnerable target
Apache MPM Worker1 thread per connectionHighThreads are cheaper than processes but still finite and blockable
Apache MPM EventAsync handling of keep-alive idle timeMediumReduces — but does not eliminate — exposure versus Prefork/Worker
Microsoft IIS (classic thread pool)1 thread per connectionHighmaxConcurrentRequestsPerCPU becomes the limiting factor
nginxEvent-driven, async I/OLowDoesn’t block a thread per connection, but worker_connections (default 1,024/worker) is still a finite ceiling
Node.js (net/http module)Async event loopLowVulnerable mainly through maxConnections and file-descriptor exhaustion, not thread exhaustion
LiteSpeedAsync, event-drivenLowBuilt-in anti-Slowloris logic since early releases
CaddyAsync (Go net/http)LowGo’s HTTP server enforces read/header timeouts more aggressively by default

Important nuance about nginx: nginx’s event-driven architecture is more resistant to classic Slowloris because idle connections do not block a worker thread. It is not immune. Each open connection — even one sending nothing — still consumes a file descriptor and a slot counted against worker_connections. An attacker with enough parallel connections can still exhaust nginx’s total connection ceiling and cause new-connection refusals, just at a higher connection count than would be needed against Apache Prefork.

Detection signals and telemetry

Slowloris produces almost no volumetric signal. Detection depends on connection-state and completion-rate metrics rather than traffic graphs.

Terminal window
# Count current ESTABLISHED connections — compare against historical baseline
ss -o state established '( dport = :80 or dport = :443 )' | wc -l
# List connections per source IP — Slowloris concentrates many connections per attacker IP
ss -tn state established | awk '{print $4}' | cut -d: -f1 | sort | uniq -c | sort -rn | head
# Apache: check worker/thread status for high "reading" or "keepalive" states
apachectl status # or: mod_status via /server-status
# nginx: check active connections vs. configured worker_connections
nginx -T | grep worker_connections
IndicatorNormalUnder Slowloris
ESTABLISHED connectionsProportional to real trafficElevated and growing, disproportionate to request rate
Header completion rate95%+ within timeout windowBelow 10–20%
Average time-to-first-byte per connectionMilliseconds to low secondsMinutes, or never completes
Requests per secondTracks user activityFlat or near zero despite high connection count
Worker/thread pool utilizationWell below capacityNear 100% with low CPU usage
CPU usage during the eventCorrelates with trafficLow — the server is idle, just waiting

The combination of high ESTABLISHED connection count + low CPU + low RPS is the clearest Slowloris signature. A volumetric attack shows the opposite: high CPU, high RPS, high bandwidth.

Mitigation techniques

1. Aggressive, phase-specific timeouts

A single global connection timeout is not precise enough — it does not catch a connection that is technically making progress (one byte every 10 seconds). Timeouts must be scoped to the header-reception phase specifically, ideally combined with a minimum data rate.

# Apache — mod_reqtimeout
# Headers must complete within 10-20s, at a minimum rate of 500 bytes/sec
RequestReadTimeout header=10-20,MinRate=500
# Body must complete within 30s at the same minimum rate
RequestReadTimeout body=30,MinRate=500
# nginx
client_header_timeout 10s;
client_body_timeout 10s;
keepalive_timeout 15s;
send_timeout 10s;

The MinRate parameter in mod_reqtimeout is the decisive setting against Slowloris specifically: a connection sending 1 byte every 10–15 seconds computes to well under 1 byte/sec, which fails a MinRate=500 check almost immediately — long before any fixed timeout would fire.

2. Per-IP concurrent connection limits

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

This caps how many sockets a single source can hold open regardless of what data — if any — is sent on them, which blocks the core mechanic of Slowloris (many parallel stalled connections from few IPs) even before any HTTP header is evaluated.

3. Prefer event-driven or async server architectures

Where architecturally possible, run nginx, LiteSpeed, or Apache’s Event MPM instead of Prefork/Worker in front of the application. Async architectures don’t eliminate the finite-connection-ceiling problem, but they remove the thread-exhaustion failure mode that makes classic Slowloris so effective against Prefork.

4. Reverse proxy / edge request buffering

Place a reverse proxy or edge network in front of the origin that fully assembles and validates each HTTP request — headers and body — before opening any connection to the origin server. Incomplete requests are held and eventually dropped at the proxy layer, which is built to absorb large numbers of concurrent slow connections; the origin only ever sees complete, valid requests. This is the most structurally robust defense because it removes the origin’s thread/connection ceiling from the attack surface entirely.

5. JA3/JA4 fingerprinting and behavioral rules at the WAF

Slowloris tooling (the original Perl script and its many derivatives) produces a TLS handshake fingerprint that differs from mainstream browsers, and it omits headers a real browser always sends (Accept-Language, Accept-Encoding) and never requests secondary page resources. A WAF that fingerprints TLS handshakes and scores request behavior can flag and drop these sessions before the HTTP layer is even evaluated.

Common mistakes

MistakeWhy it failsBetter approach
Setting only one global Timeout directiveDoesn’t catch a connection technically sending data below any meaningful rateUse phase-specific timeouts with a MinRate/minimum-throughput check
Assuming nginx is immune because it’s “async”worker_connections is still a hard ceiling; file descriptors still get exhaustedConfigure limit_conn and header timeouts even on nginx
Relying on a network firewall aloneSlowloris uses fully valid TCP connections on 80/443; there’s no packet to blockUse application-layer controls: WAF, reverse proxy timeouts, behavioral detection
Blocking by source IP after the factSlowloris can run from a single machine, and distributed variants rotate IPsCombine per-IP connection caps with edge-side request buffering
Monitoring only bandwidth/RPS dashboardsSlowloris generates no volumetric anomalyAdd ESTABLISHED-connection count and header-completion-rate to alerting

How to Implement on Azion

Azion terminates HTTP connections at the edge, ahead of the origin, which changes where a Slowloris attack’s stalled connections actually land.

  • Firewall can apply connection-rate and per-IP concurrency rules before traffic reaches the application layer
  • WAF can help detect and block slow-header request patterns and known attack-tool signatures, depending on configured rule sets
  • DDoS Protection provides always-on detection aimed at Layer 7 attack patterns, including low-and-slow connection exhaustion
  • WAAP combines WAF, DDoS protection, and bot-related controls for teams that want these layered together

Because edge nodes assemble and validate requests before opening any connection to the origin, an incomplete Slowloris request generally never reaches origin infrastructure at all — it is held and expired at the edge, which is built to absorb far more concurrent slow connections than a typical origin web server.

Frequently Asked Questions

What is a Slowloris attack? Slowloris is a denial-of-service technique that opens many HTTP connections to a server and keeps them alive by sending incomplete request headers at intervals just under the server’s timeout. It never sends the CRLF CRLF sequence that terminates a request, so the server holds each connection’s thread or slot open indefinitely while waiting for a request that never completes.

Who created Slowloris and when? Security researcher Robert “RSnake” Hansen released Slowloris as a Perl script in June 2009. It became widely known after reports of its use disrupting government web infrastructure during the 2009 Iranian election protests, and it remains a reference case in OWASP’s Denial of Service documentation.

What does CRLF CRLF mean and why does it matter? CRLF CRLF (\r\n\r\n) is the two-consecutive-line-break sequence that HTTP/1.1 uses to mark the end of a request’s header block. A server cannot begin processing a request until it sees this sequence. Slowloris deliberately withholds it, keeping the request permanently “in progress” from the server’s point of view.

Can Slowloris be run from a single computer? Yes. Slowloris does not require a botnet. A single machine with a normal internet connection can open enough parallel connections — often only a few hundred — to exhaust a default Apache server’s worker pool, because the attack’s resource cost is a handful of open sockets, not high bandwidth or CPU.

Does HTTPS/TLS stop Slowloris? No. The TLS handshake completes normally before the HTTP layer begins, and Slowloris operates entirely inside the already-encrypted HTTP exchange. Effective detection and mitigation have to happen after TLS termination — at the load balancer, reverse proxy, or WAF — where the request content is visible.

Which web servers are most vulnerable to Slowloris? Servers that allocate one thread or process per connection are most exposed: Apache’s Prefork and Worker MPMs, older IIS thread-pool configurations, and some traditional Python/Ruby application servers. Event-driven, asynchronous servers like nginx, LiteSpeed, Node.js, and Caddy are more resistant because they don’t dedicate a thread per idle connection, though they remain limited by their configured connection ceilings.

Is nginx completely immune to Slowloris? No. nginx’s async architecture avoids the thread-exhaustion failure mode, but every open connection — even one sending nothing — still consumes a file descriptor counted against worker_connections (default 1,024 per worker). An attacker with enough concurrent connections can still exhaust that ceiling. limit_conn and header timeouts should be configured on nginx too.

How is Slowloris different from a SYN flood? A SYN flood exploits the TCP handshake and leaves connections in a half-open state that never reaches the application layer. Slowloris establishes fully valid TCP connections and operates at the HTTP layer, sending real — just incomplete — application data. See What Is a SYN Flood Attack? for the transport-layer mechanism.

How is Slowloris different from a HTTP Slow Read attack? Slowloris stalls the request side of an HTTP exchange by never completing the headers a client sends. HTTP Slow Read instead sends a complete, valid request and then stalls the response by manipulating the TCP receive window so the server can only send data a few bytes at a time. Both exhaust server resources with minimal bandwidth, but they target opposite halves of the request/response cycle. See What Is a HTTP Slow Read Attack?.

Can a standard network firewall block Slowloris? Generally no. A stateless or purely network-layer firewall sees valid, well-formed TCP connections on port 80 or 443 with no malformed packets to match. Blocking Slowloris requires application-layer visibility — a WAF, a reverse proxy with phase-specific timeouts, or behavioral/fingerprint-based detection.

How many connections does a Slowloris attack typically need? It needs roughly as many connections as the target’s worker or connection pool size. Apache with default settings accepts around 150–256 simultaneous workers, so a few hundred slow connections is often enough — well within what a single consumer machine can maintain.

What is the single most effective server-side mitigation? There is no single silver bullet, but combining a minimum-data-rate timeout (RequestReadTimeout ... MinRate=500 on Apache, or equivalent) with a per-IP connection cap (limit_conn on nginx) closes both the “trickle data forever” mechanic and the “open hundreds of connections from one source” mechanic simultaneously.

Sources

  • Hansen, Robert (“RSnake”). “Slowloris HTTP DoS.” 2009.
  • OWASP. “Slowloris.” OWASP Community Pages.
  • OWASP. “Denial of Service Cheat Sheet.”
  • IETF. “HTTP/1.1 Message Syntax and Routing.” RFC 9112 (obsoletes RFC 7230).
  • Apache Software Foundation. “mod_reqtimeout.” Apache HTTP Server Documentation.
  • nginx. “Module ngx_http_limit_conn_module.” Official Documentation.
  • NIST. “Guide to Intrusion Detection and Prevention Systems.” SP 800-94.
stay up to date

Subscribe to our Newsletter

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