A few months after I started this blog, traffic surged like never before. I wrote an article that went “viral” on both Hacker News and Reddit.
As soon as it hit the front page, my small server was on the verge of collapse. Requests flooded in like a tsunami, Apache was struggling, and I was left helpless, restarting the machine over and over again – like a firefighter battling a blaze with a squirt gun.
In internet terms, this is called the “death embrace,” and it was terrifying.
At the time, I couldn't quite grasp how intense the requests were or the pressure it put on me.
Just this February, I wrote another post that landed #1 on Hacker News within minutes. This time, I was prepared. I saved the server logs and created a visualization to show exactly what my $6/month server went through.
Web Request Visualization
Each web request to the server is represented by a circle moving toward it. See the legend in the bottom right:
- Bots vs. Real Users: Based on user agent detection. Legitimate bots typically have “bot” in their name, while others are identified heuristically.
-Response Types:
- ✅ 200 OK: Successful request
- 🔄 Redirect: Represented by a wiggling dot
- ❌ 404 Not Found: Red dots falling off the screen
- 💥 Zip Bomb: More on this in a bit
My Server Specs
Despite the chaos, my little $6/month server, with only 1GB of RAM, Apache 2 + PHP, and a basic MySQL database, stood its ground.
There was no fancy cloud autoscaling, no load balancers. Just my lean setup and good caching.
Host: DigitalOcean (1GB RAM)
Web Server: Apache 2
Environment: Ubuntu + PHP
Database: MySQL
Price: $6/month
My blog runs on a custom PHP framework. Most pages are cached in memcached, so the database is queried only once per hour per page. This efficient setup has handled millions of requests in the past, including my posts about being fired by machine learning and getting featured on the BBC.
Timeline of Events
🕓 4:43 PM (PST) — Post submitted to Hacker News.
🕓 4:53 PM (PST) — Hits the front page. A swarm of bots descends.
🕔 5:17 PM (PST) — #1 on Hacker News. The floodgates open.
🕗 8:00 PM (PST) — A moderator renames the entry (reason unknown). Traffic plummets.
🕓 3:56 AM (PST) — A bot scans 300 URLs looking for vulnerabilities.
🕘 9:00 AM (PST) — Traffic surges again, mainly from the Mastodon network.
🕤 9:32 AM (PST) — Large spam attack: ~4,000 requests in a minute, mostly advertising dark web marketplaces.
🕓 4:00 PM (PST) — In 24 hours, my server handled 46,000 requests.
The Elephant in the Room
The server never crashed. In fact, the CPU usage never exceeded 16%.
But you might have noticed something odd about the visualization: my 1GB RAM server’s memory usage stayed at 50%. Why? MySQL.
When I started this blog, I ambitiously logged every single request to the database. This was useful for tracking post popularity. But 12 years later, the database had ballooned. Sorting millions of rows for simple analytics became a costly operation.
After the viral rush, I backed up the data and dropped the table. It was time.
One Way To Make a Zip Bomb
Most of the traffic on the web comes from bots. These bots are mostly good for discovering new content. Things like RSS feed readers, search engines crawling content, or nowadays AI bots supporting LLMs with content.
But there are plenty of bad bots too. These bots come from spammers, content scrapers, or hackers. At a previous employer, a bot discovered a vulnerability in WordPress and inserted a malicious script in our server. It then turned the server into a botnet for DDOS attacks. One of my early websites was completely de-listed from Google Search results because bots were generating spam on it. I had to figure out how to protect myself from these bots. That's when I started using zip bombs.
A zip bomb is a compressed file that is relatively small but expands to be very large, overwhelming a machine.
One feature that was developed early on the web was to use gzip for compression. With slow internet speeds and dense information, the idea was to compress data as much as possible before sending it. So a 50 KB HTML file made of text could be compressed to 10 KB saving 40 KB of transfer. In dial-up modem terms, this meant a page would take 3 seconds instead of 12 to download.
The same compression techniques work for CSS, JavaScript, and even images. Gzip was fast, simple, and significantly improved the browsing experience.
When browsers make web requests, they include a header that tells the target server they support compression. If the server also supports compression, it will return a compressed version of the expected data.
Accept-Encoding: gzip, deflate
Bots that crawl the web also support this feature. Since their job is to get data from all over the web, they use compression to maximize bandwidth. We can take advantage of this.
On this blog, I get frequent bots that scan for security vulnerabilities, and I usually ignore them. But when I detect that they are trying to inject malicious attacks or probe for responses, I return a 200 OK response, and I send them a gzip compressed package. I get files that are between 1MB and 10MB, and they gladly accept them.
Most of the time, I never hear from them again, even after they receive it. Why? Because they crash after receiving the file.
Content-Encoding: deflate, gzip
What happens is they get the file, read the header, and realize it's a compressed file. So they try to uncompress this 1MB file, looking for the content they need. But the file keeps growing until they run out of memory and their server crashes. The 1MB file uncompresses to 1GB. This is enough to destroy most bots.
But for those annoying scripts that don't quit, I give them the 10MB file. That file uncompresses to 10GB, and the script is instantly taken out.
Before I show you how to make one, I must warn you, your machine may crash or even be destroyed. Proceed at your own risk.
Here is how to make a Zip Bomb:
dd if=/dev/zero bs=1G count=10 | gzip -c > 10GB.gz
I will explain what this command does:
1. dd: The dd command is used to copy or convert data.
2. if: Input file, specify /dev/zero which is a special file that produces an infinite stream of zero bytes.
3. bs: Block size, set the block size to 1 gigabyte (1G), meaning dd will read and write data in chunks of 1GB at a time.
4. count=10: This tells dd to process 10 blocks, each of size 1GB. So, this will generate 10 GB of zero data.
Then, we pipe the output of the command to gzip, which will compress the output into a file called 10GB.gz. In this case, the generated file size is 10MB.
On my server, I added a middleware that checks if the current request is malicious. I set up a list of blacklisted IP addresses that repeatedly try to scan the whole website. I also set up other heuristics to detect spammers. Many spammers will try to spam a page, then come back to see if the spam made it to the page. I use the following pattern to detect them.
It looks something like this:
<?php
if(ipIsBlackListed() || isMalicious()) {
header("Content-Encoding: deflate, gzip");
header("Content-Length: " + filesize(ZIP_BOMB_FILE_10G)); //10 MB
readfile(ZIP_BOMB_FILE_10G);
exit;
}
That's it. My only expense is that I now sometimes serve a 10MB file. If my article goes viral, I compress it to 1MB, and it works just as well.
Another thing, Zip bombs are not foolproof. They are easily detected and bypassed. After all, you can only read part of the content. But for unskilled bots that blindly crawl the web and disrupt servers, it's enough to protect your server.
One Other Way To Make a Zip Bomb
First, let's up the ante and create a 10GB GZIP file of all zeros. We could make multiple compressions, but for now, let's keep it simple.
dd if=/dev/zero bs=1M count=10240 | gzip > 10G.gzip
As you can see, it's 10G. We could do better, but for now, that's enough.
Now that we have successfully created this thing, let's set up a PHP script that will serve it to the client.
<?php
//prepare the client to recieve GZIP data. This will not be suspicious
//since most web servers use GZIP by default
header("Content-Encoding: gzip");
header("Content-Length: ".filesize('10G.gzip'));
//Turn off output buffering
if (ob_get_level()) ob_end_clean();
//send the gzipped file to the client
readfile('10G.gzip');
Okay, that's it!
So we can use it as a simple defense, like this:
<?php
$agent = filter_input(INPUT_SERVER, 'HTTP_USER_AGENT');
//check for nikto, sql map or "bad" subfolders which only exist on wordpress
if (strpos($agent, 'nikto') !== false || strpos($agent, 'sqlmap') !== false || startsWith($url,'wp-') || startsWith($url,'wordpress') || startsWith($url,'wp/'))
{
sendBomb();
exit();
}
function sendBomb(){
//prepare the client to recieve GZIP data. This will not be suspicious
//since most web servers use GZIP by default
header("Content-Encoding: gzip");
header("Content-Length: ".filesize('10G.gzip'));
//Turn off output buffering
if (ob_get_level()) ob_end_clean();
//send the gzipped file to the client
readfile('10G.gzip');
}
function startsWith($a, $b) {
return strpos($a, $b) === 0;
}
As we said above, this script is obviously not the yellow of the egg, but it can defend against those script kiddies I mentioned earlier who don't know that all these tools have parameters to change the user agent.
Fighting Back Against Content Thieves Using Zip Bombs!
You might have noticed the little explosion in the animation. Let me explain.
One day, I found a website that was live-stealing my content. Whenever someone visited their page, they would scrape my blog post, replace my branding, and claim it as their own.
At first, I fought back manually by feeding them fake data. But that quickly became useless. So I pulled out my secret weapon: giving it a zip bomb.
When their bot visited my site, I fed it a very small compressed file. Their server eagerly downloaded and uncompressed it, only to be met with several GBs of chaotic data. 💥 Bang Bang Bang! Game over.
Over the years, zip bombs have become my shield against bots trying to scrape, exploit, or abuse my site.
Lessons Learned For You
Dang (the Hacker News mod) will change your article titles, and there's nothing we can do about it.
Most traffic is from bots, not humans. They are constantly scanning, scraping information, and sending spam.
Apache worker thread limits matter. I found that two running instances had a maximum worker thread limit of 75, which I hadn't noticed before.
An optimized, lightweight setup beats expensive infrastructure. With proper caching, a $6/month server can withstand tens of thousands of visits – no Kubernetes needed.
Conclusion
This baptism by fire taught me more than just server management.
Watching the web request visualization unfold gave me new insights into how traffic flows, how bots operate, and how even the simplest optimization decisions can make the difference between a server crashing or running smoothly.
Most importantly: if you're going viral, be prepared~
Author: Da Xiong in Action
References:
https://idiallo.com/blog/pc-is-not-dead-no-need-for-new-ones
https://github.com/ibudiallo/reqvis
Related Readings:
Must-Read for Data Professionals: I Crawled 100 Billion Web Pages Before Realizing That Web Scraping Is Not Simple At All
CTO and Programmer Both Sentenced to Prison Because of Out-of-Control Crawlers
Scraping Web Content with Node.js