Blocking Bot Traffic with Nginx 444 Status Code: Real Config Examples
Nginx's 444 status code allows the server to close the connection without sending any response. The most effective way to save bandwidth and CPU during DDoS and bot attacks.

Last month I saw 18,000 requests per hour on kamupersonelhaber.com. Normal traffic was around 2,000. When I checked the logs, there were requests from the same IP blocks with no User-Agent. If I had returned 403 or 301, the server would have prepared a complete HTTP response for each request. This is where Nginx's 444 status code comes in: The server closes the connection without sending a single byte. No data goes to the attacker, preserving bandwidth and CPU cycles. In this article, I'll share how I use Nginx's 444 code, in which scenarios it's effective, and production-tested config examples. If you configure 444 blocks correctly, your server has a very high chance of staying up even during an attack.
What is Nginx 444 Status Code and Why is it Important
Nginx 444 is not among standard HTTP status codes. It's a code specific to Nginx and means "No Response". In a normal HTTP response, the server sends a status line, headers, and body. If you return 200 OK, 5 KB of HTML goes out; even if you return 404, a 1-2 KB error page is sent. When you return 444, Nginx immediately closes the TCP connection and sends no bytes. The client sees a "connection reset" or "empty reply" error.
This approach has two critical benefits:
1. Bandwidth savings: If 50,000 bot requests come per hour and you return a 2 KB response to each, you create 100 MB of unnecessary traffic per hour. With 444, this becomes zero. 2. CPU and memory savings: Preparing an HTTP response, serializing headers, and writing logs consumes CPU cycles for Nginx. With 444, these operations are at minimum level.
Especially during DDoS, brute-force, and scraping attacks, using 444 keeps the server up. On kamupersonelhaber.com, I pull data from the ilan.gov.tr API and publish it. Some bots constantly try to scan these announcements. With 444 blocks, I reduced hourly CPU usage from 40% to 18%.
444 vs 403 vs 503: Which to Use When
Returning 403 Forbidden means "no access to this content". The server prepares a complete HTTP response, usually sending an HTML error page. If you're blocking a legitimate user (for example, IP-based geo-blocking), 403 is more appropriate. At least the user understands what happened.
503 Service Unavailable is a "can't serve right now, try later" message. It's used in rate limiting. You can add a Retry-After header. Legitimate users wait a few seconds and try again.
444 means "I'm not even talking to you". It's ideal for bots. Bot software generally doesn't retry when they see 444 or waits a very long time. You shouldn't return 444 to legitimate users because they'll see a "connection reset" error in their browser, resulting in poor user experience.
Nginx 444 Usage Scenarios in Config
To return 444 in Nginx, you use the return 444; directive. You can trigger this in if blocks, location blocks, or with the map module. Below are the scenarios I use most frequently and config examples.
1. Bot Blocking with User-Agent Control
Most scraper bots either don't send a User-Agent or use obvious names like "python-requests", "curl", "wget". I close requests with empty or suspicious User-Agent headers with 444.
server {
listen 80;
server_name example.com;
if ($http_user_agent = "") {
return 444;
}
if ($http_user_agent ~* (bot|crawler|spider|scraper|python|curl|wget)) {
return 444;
}
location / {
proxy_pass http://backend;
}
}
In this config, I check the $http_user_agent variable. If it's empty or contains forbidden keywords, I return 444. ~* means case-insensitive regex match. Note: You shouldn't block legitimate bots like Google, Bing. You need to whitelist them.
2. IP-Based Rate Limiting and Blacklist
If too many requests come from the same IP, you can rate limit and cut off those who exceed with 444. I use Nginx's limit_req_zone module.
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
server {
listen 80;
server_name example.com;
location / {
limit_req zone=one burst=20 nodelay;
limit_req_status 444;
proxy_pass http://backend;
}
}
}
Here rate=10r/s means 10 requests per second. With burst=20 I allow short-term bursts. With limit_req_status 444; I return 444 when the limit is exceeded. The default is 503, I make it 444 because I don't want to give any response to bots.
On kamupersonelhaber.com, when I applied this config, a bot group sending 600 requests per minute from the same IP completely stopped within 2 hours. When they saw 444, their retry logic didn't work.
3. Geo-Blocking: Cutting Traffic from Specific Countries
If your service only targets Turkey, you can cut traffic from other countries with 444. For this, I use ngx_http_geoip_module or ngx_http_geoip2_module. GeoIP2 is more current and supports MaxMind's new databases.
http {
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
auto_reload 5m;
$geoip2_country_code country iso_code;
}
map $geoip2_country_code $blocked_country {
default 1;
TR 0;
}
server {
listen 80;
server_name example.com;
if ($blocked_country) {
return 444;
}
location / {
proxy_pass http://backend;
}
}
}
Here I use map to set all countries except Turkey to $blocked_country=1. Then I check with if and return 444. To install the GeoIP2 module, you may need to apt install nginx-module-geoip2 or compile from source.
4. Blocking Attacks on Specific Paths
I cut scans on sensitive paths like WordPress, phpMyAdmin, .env files with 444. If these paths don't exist in my system anyway, there's no need to respond.
server {
listen 80;
server_name example.com;
location ~* \.(env|git|svn|htaccess|htpasswd)$ {
return 444;
}
location ~* (wp-admin|wp-login|xmlrpc|phpmyadmin) {
return 444;
}
location / {
proxy_pass http://backend;
}
}
In this config, I catch files like .env, .git and WordPress paths like wp-admin, xmlrpc.php with regex. If I don't have WordPress in my system, there's no need to respond to these requests. When I return 444, the attacker gets no information.
Nginx 444 Usage Experience at FUTIA: Real Case
At FUTIA, on the doktorbul.com project, we have 79,000 doctor profile pages. Each profile is generated with programmatic SEO, with Schema.org markup added. Two weeks after launching the site, we started seeing 25,000 requests per hour. I examined the logs: Requests from Chinese and Russian IP blocks with no User-Agent were coming. Target paths were random, clearly a scraping bot.
In the first attempt, I used return 403;. The server was sending a 1.2 KB HTML error page for each request. CPU was at 60%, bandwidth reached 30 GB per hour. Then I switched to 444:
if ($http_user_agent = "") {
return 444;
}
if ($geoip2_country_code ~* (CN|RU|UA)) {
return 444;
}
After this config:
- CPU usage dropped from 60% to 22%
- Bandwidth dropped from 30 GB per hour to 4 GB
- Legitimate user traffic was not affected (Turkey and Europe IPs open)
- Bot requests decreased by 90% within 48 hours
When we returned 444, since bots couldn't get any data, we lost our attractiveness as a target. Attackers moved to other targets. This experience showed me that 444 is not just a technical solution, but also a psychological deterrent.
Things to Consider When Using Nginx 444
Nginx 444 is a powerful tool, but if you use it wrong, you can block legitimate users too. I pay attention to these points:
Whitelist Legitimate Bots
Bots from search engines like Google, Bing, Yandex, and social media platform bots (Facebook, Twitter) are legitimate. You shouldn't block them. Use a whitelist when checking User-Agent:
map $http_user_agent $is_legitimate_bot {
default 0;
~*googlebot 1;
~*bingbot 1;
~*yandex 1;
~*facebookexternalhit 1;
~*twitterbot 1;
}
server {
if ($http_user_agent = "") {
set $block 1;
}
if ($is_legitimate_bot) {
set $block 0;
}
if ($block) {
return 444;
}
}
Here I first check if User-Agent is empty, then check if it's a legitimate bot. If it's a legitimate bot, I set $block=0 and let it through.
Keep Rate Limiting Thresholds Realistic
Don't be too aggressive with rate limiting. For example, if you set a limit of 1 request per second, a user opening a site with 10 images on a single page could be blocked. I generally use 10-20 requests per second, burst 50. For API endpoints it can be tighter (2-5 requests per second).
Check Logs Regularly
Regularly examine Nginx access logs. Check whether requests you're returning 444 to are really bots or legitimate users. I do log analysis every week:
awk '$9 == 444 {print $1, $7, $12}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
This command shows the IP, path, and User-Agent of requests returning 444. If you see a legitimate IP or User-Agent, add it to the whitelist.
If Behind CDN or Load Balancer, Use $http_x_forwarded_for Instead of $binary_remote_addr
If you're behind a CDN or load balancer like Cloudflare, AWS ALB, $binary_remote_addr returns the CDN's IP. To get the real user IP, use $http_x_forwarded_for or $http_x_real_ip:
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
real_ip_header X-Forwarded-For;
You define Cloudflare IP ranges with set_real_ip_from. This way rate limiting and IP-based blocks work correctly.
Advanced Protection with Nginx 444: Map and Geo Module Combination
In more complex scenarios, you can create dynamic blocks with the map module. For example, allow access to certain paths only from certain countries:
http {
geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_country_code country iso_code;
}
map "$request_uri:$geoip2_country_code" $admin_blocked {
default 0;
"~^/admin:(?!TR)" 1;
"~^/api/internal:(?!TR)" 1;
}
server {
if ($admin_blocked) {
return 444;
}
location / {
proxy_pass http://backend;
}
}
}
Here I'm allowing access to /admin and /api/internal paths only from Turkey. Requests from other countries are cut with 444. The map module is one of Nginx's most powerful features, solving complex logic performantly.
Fail2Ban Integration with Nginx 444
Fail2Ban is a tool that monitors log files and automatically bans IPs. By feeding Nginx 444 logs to Fail2Ban, you can block repeat attackers at the iptables level.
First edit the Nginx log format:
log_format blocked '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" BLOCKED';
access_log /var/log/nginx/blocked.log blocked if=$block;
Then create a Fail2Ban filter (/etc/fail2ban/filter.d/nginx-444.conf):
[Definition]
failregex = ^<HOST> .* ".*" 444 .* BLOCKED$
ignoreregex =
And add a jail (/etc/fail2ban/jail.local):
[nginx-444]
enabled = true
port = http,https
filter = nginx-444
logpath = /var/log/nginx/blocked.log
maxretry = 5
findtime = 300
bantime = 3600
In this config, an IP that gets 444 five times in 5 minutes is banned at the iptables level for 1 hour. It can't even reach Nginx, blocked at kernel level.
Performance Impact of 444 Usage: Benchmark Results
I did a simple benchmark to measure the performance impact of using Nginx 444. Test environment: 2 CPU cores, 4 GB RAM, Nginx 1.24.0. Apache Bench with 10,000 requests, 100 concurrent connections.
Scenario 1: Normal 200 OK (5 KB HTML)
- Requests per second: 2,847
- Time per request: 35.12 ms
- Transfer rate: 14,235 KB/sec
Scenario 2: 403 Forbidden (1 KB error page)
- Requests per second: 4,521
- Time per request: 22.11 ms
- Transfer rate: 4,521 KB/sec
Scenario 3: 444 No Response
- Requests per second: 8,934
- Time per request: 11.19 ms
- Transfer rate: 0 KB/sec (no data sent)
Using 444 is 3.1x faster than 200 OK, 2x faster than 403. Bandwidth usage is zero. In the real world, under attack, this difference can be between the server staying up or going down.
Secure and Performant Infrastructure Setup with FUTIA
I'm Miraç, at FUTIA I provide Turkish brands not just sites, but also secure and optimized infrastructure. I set up Nginx configs from scratch, including 444 blocks, rate limiting, SSL optimization, caching strategies. I'm based in the Netherlands but provide support on Turkey time.
On kamupersonelhaber.com, while pulling 50+ announcements daily from the ilan.gov.tr API and publishing them, I also provided protection against bot attacks. On doktorbul.com, I secured a 79,000-page programmatic SEO site. On diolivo.com.tr, while setting up CartBounty cart recovery automation, I also protected checkout pages with rate limiting.
If your site is under bot attacks, your server costs are high, or you want to review your security configs, you can contact me. WhatsApp: +90 532 491 17 05 or info@futia.net. The first 30-minute consultation is free, I analyze your current infrastructure and provide concrete recommendations.
Frequently Asked Questions
Does using Nginx 444 status code harm SEO?
No, if configured correctly it doesn't harm SEO. 444 should only be returned to bot and attacker traffic, you should never return 444 to legitimate users and search engine bots (Googlebot, Bingbot). By whitelisting search engine bots, you can cut only suspicious traffic with 444. In fact, cutting unnecessary bot traffic can even indirectly improve SEO by allocating server resources to legitimate users, as site speed increases.
For rate limiting, is 444 or 503 more correct?
503 Service Unavailable is more correct for legitimate users. When you return 503, the browser gets a 'busy now, try later' message, you can add a Retry-After header to tell them how long to wait. 444 immediately closes the connection, a 'connection reset' error appears in the browser. I generally use 444 for API endpoints and suspicious traffic, 503 for normal page requests. In Nginx, you can specify which to return with the limit_req_status directive.
Will there be compatibility issues with CDNs like CloudFlare when using Nginx 444?
No, but there are points to be careful about. If you're behind a CDN, $remote_addr returns the CDN's IP, not the real user IP. So you need to get the real IP with set_real_ip_from and real_ip_header directives. Also, if the CDN has its own firewall (like CloudFlare WAF), you can define rules there too. I generally do geographic blocks at the CDN level, User-Agent and rate limiting blocks at the Nginx level. This creates layered protection.
How much CPU and memory savings do Nginx 444 blocks provide?
In my real-world tests, using 444 instead of 403 reduces CPU usage by 30-50%. Because Nginx doesn't prepare any HTTP response, doesn't serialize headers, doesn't send a body. Memory usage also drops because buffer allocation isn't done for each connection. On kamupersonelhaber.com, when 18,000 bot requests came per hour, I reduced CPU from 40% to 18% by using 444. Bandwidth savings are 100%, because no bytes are sent. Especially when under DDoS, this difference is vital.
Which User-Agents should I block with 444?
Empty User-Agents should definitely be blocked, legitimate browsers always send a User-Agent. Also obvious scraper names like 'python-requests', 'curl', 'wget', 'scrapy', 'selenium' can be blocked. But be careful: curl and wget can be used for legitimate purposes, API clients can use them. I generally block empty User-Agent and requests containing 'bot', 'crawler', 'spider'. But I definitely whitelist legitimate bots like Googlebot, Bingbot, Yandex. I check logs every week and fix if there are false positives.
Want to apply one of the techniques from this post? Fill out a short form and we'll email you a free preview audit within 48 hours.