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\nHost: example.com\r\nUser-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\nHost: target.com\r\nUser-Agent: Mozilla/5.0\r\nX-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 parallelEach 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 eacht=1-10s Sockets fill Apache's worker pool (150-256 workers occupied)t=10s+ Legitimate requests queue; new connections refused or time outt=10-15s Attacker sends 1 keep-alive byte per stalled socket, resets timerst=∞ Cycle repeats — server remains saturated until attack stopsSlowloris 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.
| Characteristic | Slowloris | HTTP Flood | HTTP Slow Read |
|---|---|---|---|
| HTTP phase exploited | Request headers (never completed) | None — requests are complete and valid | Response delivery (client reads slowly) |
| Resource exhausted | Worker threads / connection slots | CPU, database, application logic | Server send buffer + connection slots |
| Request volume | 1 request per connection, incomplete | Very high — thousands of complete req/s | 1 complete request per connection |
| Bandwidth signature | Near-zero, flat | High and often bursty | Near-zero, flat |
| TCP window manipulation | Not used | Not used | Central to the mechanism |
| Detectable by rate limiting | No — no request rate to limit | Yes — request/sec limiting works | No — no request rate to limit |
| Connections needed to succeed | Hundreds (thread-per-connection servers) | Thousands (varies by app cost) | Hundreds |
| Typical origin defense | Timeouts, limit_conn, async server | WAF rate limiting, CAPTCHA, rules | Send-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 model | Default connection handling | Slowloris exposure | Notes |
|---|---|---|---|
| Apache MPM Prefork | 1 process per connection | High | MaxRequestWorkers (default 150–256) exhausts fast; historically the reference vulnerable target |
| Apache MPM Worker | 1 thread per connection | High | Threads are cheaper than processes but still finite and blockable |
| Apache MPM Event | Async handling of keep-alive idle time | Medium | Reduces — but does not eliminate — exposure versus Prefork/Worker |
| Microsoft IIS (classic thread pool) | 1 thread per connection | High | maxConcurrentRequestsPerCPU becomes the limiting factor |
| nginx | Event-driven, async I/O | Low | Doesn’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 loop | Low | Vulnerable mainly through maxConnections and file-descriptor exhaustion, not thread exhaustion |
| LiteSpeed | Async, event-driven | Low | Built-in anti-Slowloris logic since early releases |
| Caddy | Async (Go net/http) | Low | Go’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.
# Count current ESTABLISHED connections — compare against historical baseliness -o state established '( dport = :80 or dport = :443 )' | wc -l
# List connections per source IP — Slowloris concentrates many connections per attacker IPss -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" statesapachectl status # or: mod_status via /server-status
# nginx: check active connections vs. configured worker_connectionsnginx -T | grep worker_connections| Indicator | Normal | Under Slowloris |
|---|---|---|
| ESTABLISHED connections | Proportional to real traffic | Elevated and growing, disproportionate to request rate |
| Header completion rate | 95%+ within timeout window | Below 10–20% |
| Average time-to-first-byte per connection | Milliseconds to low seconds | Minutes, or never completes |
| Requests per second | Tracks user activity | Flat or near zero despite high connection count |
| Worker/thread pool utilization | Well below capacity | Near 100% with low CPU usage |
| CPU usage during the event | Correlates with traffic | Low — 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/secRequestReadTimeout header=10-20,MinRate=500# Body must complete within 30s at the same minimum rateRequestReadTimeout body=30,MinRate=500# nginxclient_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
| Mistake | Why it fails | Better approach |
|---|---|---|
Setting only one global Timeout directive | Doesn’t catch a connection technically sending data below any meaningful rate | Use 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 exhausted | Configure limit_conn and header timeouts even on nginx |
| Relying on a network firewall alone | Slowloris uses fully valid TCP connections on 80/443; there’s no packet to block | Use application-layer controls: WAF, reverse proxy timeouts, behavioral detection |
| Blocking by source IP after the fact | Slowloris can run from a single machine, and distributed variants rotate IPs | Combine per-IP connection caps with edge-side request buffering |
| Monitoring only bandwidth/RPS dashboards | Slowloris generates no volumetric anomaly | Add 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.
Related Resources
- Low and Slow Attacks
- What Is a HTTP Slow Read Attack?
- What Is a DDoS Attack?
- What Is an HTTP Flood Attack?
- Azion WAF
- Azion DDoS Protection
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.