Understanding the recent DDoS attack against Read the Docs
Analysis of the Recent DDoS Event at Read the Docs
In mid-to-late June 2026, Read the Docs encountered the most complex and massive distributed denial-of-service (DDoS) assault in the organization's history. This event lasted nearly ten days, pushing their incident response protocols, edge security, and core infrastructure to the absolute limit.
At the height of the onslaught, the system was bombarded with over 5.5 million requests per minute. To put this in perspective, the traffic volume was roughly 100 times their standard baseline.
Unlike previous, simpler "traffic floods," this attack was highly adaptive, widely distributed, and specifically engineered to bypass caching mechanisms. Now that the operations team has recovered their sleep schedules, they have shared an anatomy of the attack.
The Evolution of the Threat
Historically, Read the Docs maintained a permissive stance toward bots and spiders to ensure documentation remained scrapable. Most abuse was handled via simple IP-based rate limiting. However, the landscape shifted roughly two years ago with the rise of AI crawlers.
Simple IP blocking is enough. AI-generated scrapers utilizing proxy networks are now the norm.
Comparison of Traffic Scales
| Metric | Normal Baseline | June 2026 Peak |
|---|---|---|
| Requests per Minute | ||
| Source Distribution | Predictable/Regional | Global/Millions of IPs |
| Attack Vector | Simple Scraping | Adaptive Cache Evasion |
Anatomy of the Attack
The June event was significantly more sophisticated than previous encounters. The attackers employed several high-level strategies:
- Extreme Volume: As noted, the peak reached 5.5M requests/min.
- Global Dispersion: Malicious traffic originated from millions of unique IPs across hundreds of different Autonomous System Numbers (ASNs), spanning both residential blocks and various hosting providers.
- Signature Evasion: To bypass filters like
JA3orJA4, attackers randomized:- HTTP request headers.
- TLS connection parameters.
- CDN Limitations: While Cloudflare filtered out "known botnets," a substantial portion of the traffic appeared legitimate enough to pass through to the WAF and rate-limiting layers.
- Cache Bypassing: Attackers specifically targeted URLs that trigger cache misses, such as:
404errors (non-existent pages with unique paths).302temporary redirects.
- Adaptive Tactics: The botnet monitored the defenses. When a specific path or IP range was blocked, the attackers rotated their target paths and spread traffic across wider IP pools.
Attack Flow Visualization
Scale and Breadth of Impact
Previous attacks were usually concentrated (specific countries or browser signatures). This attack, however, hit every region simultaneously, making per-colo rate limiting in Cloudflare incredibly difficult.
One specific pain point was a hardcoded Nginx redirect. While a simple rewrite regex directive can typically handle thousands of requests per second, the sheer volume of the attack caused dropped requests even on horizontally scaled hardware.
Targeted Properties included:
- Public Community Documentation
- Commercial/Business Hosted Docs
- Author-facing login dashboards
The Dilemma of "Under Attack Mode"
The team considered enabling Cloudflare's "Under Attack Mode," which forces a JavaScript challenge on all visitors. However, they rejected this because:
- It would break all API integrations.
- It would create significant friction for hundreds of thousands of legitimate readers.
Defense and Mitigation Strategies
The team relied on a combination of targeted challenges, increased caching, and pushing logic to the edge.
“There's no way we could have handled this attack without Cloudflare.”
To manage their complex defense layers, they utilized Terraform to deploy dozens of rate-limiting rules. These rules were based on a combination of:
- IP addresses and ASNs.
- Browser fingerprints.
- Specific hostnames and subdomains.
Example of Infrastructure-as-Code approach:
# Conceptual Terraform snippet for rate limiting
resource "cloudflare_filter" "ddos_mitigation" {
zone_id = var.zone_id
expression = "(http.request.uri.path contains '/non-existent' and ip.src.asn in {12345, 67890})"
}
resource "cloudflare_rate_limit" "limit_cache_misses" {
zone_id = var.zone_id
filter_id = cloudflare_filter.ddos_mitigation.id
period = 60
threshold = 100
action = "block"
}
By avoiding a blanket JS challenge and instead using granular, code-managed rules, Read the Docs managed to maintain availability for the vast majority of its users throughout the ten-day ordeal.