Understanding Slowloris DoS in Node.js: A Deep Dive into CVE-2018-12122 & CVE-2019-5739
A detailed, safe, developer-friendly explanation of the Slowloris DoS vulnerabilities in Node.js, their root causes, how the bypass was discovered, and what modern teams can learn about protecting availability.
Disclaimer
This article is strictly educational. It explains historical vulnerabilities in Node.js and the Slowloris class of Denial-of-Service attacks to help developers design safer systems. No destructive instructions are provided.
Introduction
Denial-of-Service attacks rarely receive the same attention as vulnerabilities that affect confidentiality or integrity. Yet real-world incidents continually remind engineering teams that availability is just as critical to security. A well-timed outage can disrupt business operations, degrade user trust, and, in some sectors, cause financial or regulatory harm.
One of the most fascinating examples of low-bandwidth, high-impact DoS techniques is the Slowloris attack - a method that can degrade a server not through massive bandwidth, but through careful, minimal, slow-drip requests.
A particularly interesting chapter in the Slowloris story unfolded inside the Node.js ecosystem, leading to two vulnerabilities:
- CVE-2018-12122 (original Slowloris-related issue)
- CVE-2019-5739 (bypass discovered after the initial fix)
The vulnerabilities were first reported by security researchers underflow0 and mpracucci, and their findings highlight not only how DoS works at a systems level, but also how subtle design decisions inside server frameworks can become key factors in resource exhaustion.
This writeup explores the Slowloris technique, why Node.js was affected, how the vulnerabilities were fixed, and what modern engineering teams can learn from the incident.
What Is a Slowloris DoS?
Most traditional DoS attacks function by overwhelming a target with excessive bandwidth or high request volume. Slowloris takes the opposite approach. Rather than blasting a server with data, it focuses on:
- Opening many simultaneous connections
- Sending HTTP request headers extremely slowly
- Consuming server resources without finishing the request
The key idea:
Maintain a large number of half-open HTTP requests that force the server to keep sockets alive.
Since each connection must remain active until the request is complete, these partially open requests eventually exhaust available:
- file descriptors
- socket memory
- connection slots
And once that happens, legitimate users may be unable to connect.
Why is Slowloris unusual?
Slowloris requires very little bandwidth.
Just a few kilobytes sent slowly and consistently can tie up a server in ways a massive flood might not.
From a learning perspective, Slowloris is an excellent demonstration that:
- Availability failures often emerge from resource management.
- Even high-performance, event-driven servers can be affected by socket exhaustion.
- Application-layer attacks do not require amplification or distributed traffic.

Why Node.js Was Affected
Node.js uses a non-blocking event-driven architecture. Unlike older servers (e.g., pre-thread-pool Apache) that allocate a thread per connection, Node.js:
- Uses a single-threaded event loop
- Allows thousands of sockets to remain open without blocking one another
At first glance, Node.js should be far less vulnerable to Slowloris-style attacks. After all, the event loop doesn’t “wait” for slow clients - it simply queues other events.
However, there are two critical resource constraints every OS enforces:
- Memory per open socket
- Maximum file descriptors available to a process (often 1024 on many Linux distros by default)
Even if Node.js does not block on slow clients, it still must:
- allocate a file descriptor
- maintain socket buffers
- track state for each connection
Thousands of slow, half-open HTTP connections can still exhaust a Node-based server without requiring high bandwidth.
In short
Node.js wasn’t vulnerable because it blocked on slow data.
Node.js was vulnerable because open connections always consume system resources.

The Original Vulnerability: CVE-2018-12122
In late 2018, the Node.js project received a report detailing how Slowloris-style attacks were still viable despite the event-loop design. The issue stemmed from how Node.js handled incoming HTTP headers.
Root Cause
Node.js allowed a client to send HTTP request headers indefinitely slowly, without enforcing a strict upper bound on how long the headers could take to arrive.
Thus:
- A Slowloris attacker opens many connections
- Sends header bytes one tiny piece at a time
- Keeps each connection alive for minutes
- Eventually exhausts file descriptors or memory
The Fix
Node introduced a new server property:
This setting defines how long the server will wait for all headers to be received.
If the client takes too long, Node terminates the connection.
This effectively mitigated the original Slowloris vector.
The Bypass: CVE-2019-5739
Security researcher mpracucci later discovered a clever bypass in 2019. It leveraged a behavior many developers rely on daily: HTTP keep-alive.
Understanding Keep-Alive
Normally, a client sends:
This tells the server to keep the TCP connection open after the response, so subsequent requests can reuse the same connection without creating new TCP sessions.
This improves performance, but introduced an unexpected edge case.
How the Bypass Worked
The key oversight: Node.js only enforced the header timeout on the first request made over a TCP connection.
This meant:
- The attacker sends a normal request with a
Connection: keep-aliveheader. - The server processes the request.
- Now the attacker sends a second request over the same TCP connection.
- The header timeout was not reset for the second request.
- The attacker can now slowly drip headers again - bypassing the original fix.
Why this matters
This demonstrates that DoS issues often arise not from obvious mistakes, but from edge cases in complex connection lifecycles.

The Final Fix
To fully resolve the vulnerability, Node.js changed its behavior:
The header timeout counter now resets for every request, even when using keep-alive connections.
This ensures:
- First request? Timeout enforced.
- Second request? Timeout enforced.
- Tenth request? Timeout still enforced.
This closed the loophole and restored proper protection.
Impact and Severity
Although Slowloris attacks are not typically accepted in bug bounty programs unless targeting a specific misconfiguration, their security impact remains clear.
What this vulnerability affects
- Availability of HTTP services
- Stability of Node.js web applications
- Underlying system resources (file descriptors / memory)
Severity
The bypass (CVE-2019-5739) received a CVSS score of 7.5 (High):
| Metric | Value |
|---|---|
| Attack Vector | Network |
| Attack Complexity | Low |
| Privileges Required | None |
| User Interaction | None |
| Scope | Unchanged |
| Confidentiality | None |
| Integrity | None |
| Availability | High |
For real-world systems where uptime matters - especially APIs, SaaS platforms, and internal services - even a short outage can be highly impactful.
Why Slowloris Still Matters Today
Despite being more than a decade old, Slowloris remains relevant because:
- Connection limits and file descriptors are universal constraints
- Application-layer DoS does not require large traffic
- Modern servers often support many protocols with complex parsing
- Keep-alive and pipelining change attack boundaries
More importantly, Slowloris demonstrates what defenders must always remember:
Availability vulnerabilities arise from design assumptions, not just coding mistakes.
If a server assumes:
- clients send data promptly
- connections will close quickly
- headers always arrive within a reasonable time
…then a Slowloris-style attack may exploit these assumptions.
Defensive Best Practices
The Node.js fixes eliminated this specific vulnerability, but teams should still apply general defensive measures to reduce DoS risk.
1. Enforce Reasonable Timeouts
Set:
headersTimeoutrequestTimeoutkeepAliveTimeout
These parameters help protect the server from slow, lingering connections.
2. Limit Maximum Connections
At the load-balancer or reverse-proxy layer:
- Cap max concurrent connections
- Limit slow clients
- Use queueing strategies
Tools like NGINX, HAProxy, or cloud load balancers excel at this.
3. Deploy Rate Limiting
Application-layer throttling protects against malformed or excessive inbound requests.
4. Place a Reverse Proxy in Front of Node.js
Node is excellent for application logic, but not ideal as an internet-facing edge server.
A robust reverse proxy handles:
- slow client mitigation
- header parsing
- connection pooling
- failover
5. Monitor Socket Usage
Set up alerts around:
- open file descriptors
- socket counts
- memory usage tied to connections
Spikes indicate potential degradation.

Safe Illustration: Why Timeouts Matter
Below is a harmless snippet showing how a server might set safer defaults:
This code does not defend against exploitation alone - it simply shows how timeout configuration works conceptually.
What This Teaches About Secure System Design
Slowloris is a perfect example of how non-obvious behavior can produce high-impact vulnerabilities. It teaches teams that:
- Assume inputs may be slow or malformed.
- Assume legitimate features (keep-alive) can be abused.
- Validate lifecycle states - not just the first request in a connection.
- Understand your runtime’s resource model.
- Plan for failure scenarios in event-driven architectures.
It also emphasizes that availability is a form of security - not an afterthought.
Table: Key Differences Between the Original Vulnerability and the Bypass
| Issue | CVE-2018-12122 | CVE-2019-5739 |
|---|---|---|
| Trigger | Slow header delivery on first request | Slow header delivery on subsequent keep-alive requests |
| Root cause | No header timeout enforced | Timeout not reset after first request |
| Complexity | Low | Low |
| Patch fix | headersTimeout introduced |
Reset timeout per request |
| Impact | DoS | DoS |
Final Thoughts
The Node.js Slowloris vulnerabilities demonstrate how complex interactions inside HTTP servers can create subtle but powerful DoS conditions. Even when a fix is introduced, edge cases like persistent connections (keep-alive) must be considered.
For engineering teams, Slowloris is more than a historical bug - it is a case study in resilience. It shows that:
- Application-layer attacks don’t need extreme bandwidth
- Resource limits are part of security design
- Timeouts are an essential defensive tool
- Fixes must account for connection lifecycle nuances
By applying strict timeout policies, deploying robust reverse proxies, and understanding how servers allocate resources, organizations can significantly reduce exposure to availability threats.