API’lerinize gün içinde kaç istek geldiğini hiç merak ettiniz mi? Çoğu zaman bu trafiğin önemli bir kısmı botlar, scraperlar veya kötü niyetli aktörler tarafından üretiliyor. Login sayfanıza dakikada yüzlerce istek geliyorsa birisi brute force deniyor demektir. Nginx’in yerleşik rate limiting modülü bu tür saldırılara karşı son derece etkili bir kalkan oluşturuyor. Üstelik harici bir araç kurmaya gerek yok, her şey Nginx konfigürasyonu içinde halledilebiliyor.
Rate Limiting Nedir ve Neden Önemlidir
Rate limiting, bir istemcinin belirli bir zaman diliminde sunucunuza yapabileceği istek sayısını kısıtlayan mekanizmadır. Bu mekanizma olmadan, tek bir IP adresi saniyede binlerce istek göndererek hem sunucunuzu yorabilir hem de kullanıcı hesaplarını brute force ile tehdit edebilir.
Gerçek dünyada karşılaşılan başlıca senaryolar şunlardır:
- Login brute force saldırıları: Birisi kullanıcı adı ve şifre kombinasyonlarını otomatik olarak deniyor
- API scraping: Rakipleriniz veya botlar API’nizi aşırı yükleyerek verilerinizi çekmeye çalışıyor
- DDoS amplifikasyonu: Düşük bant genişliğiyle yüksek kaynak tüketimi yaratmaya çalışan saldırılar
- Credential stuffing: Başka servislerden sızdırılmış kullanıcı adı/şifre kombinasyonlarını deneme
Nginx’in ngx_http_limit_req_module modülü bu senaryoların hepsine karşı etkili bir çözüm sunuyor ve modern Nginx kurulumlarının büyük çoğunluğunda varsayılan olarak derlenmiş geliyor.
Temel Kavramlar: Zone ve Burst
Nginx rate limiting iki temel parametre üzerine kuruludur.
Zone (Bölge): İstek sayaçlarının tutulduğu paylaşımlı bellek alanıdır. Her benzersiz anahtar (genellikle IP adresi) için bu bölgede bir kayıt tutulur.
Burst: Anlık trafik artışlarına tolerans göstermek için kullanılan tampon değerdir. Bir kullanıcının kısa süreliğine rate limit’i aşmasına izin verir ancak bu aşım belirli bir sınırda tutulur.
nodelay: Burst kuyruğundaki isteklerin hemen işlenmesini sağlar, aksi halde istekler sırayla ve yavaşça işlenir.
İlk Konfigürasyon: Global Rate Limit Zone Tanımlama
Rate limiting için önce nginx.conf dosyasının http bloğunda zone tanımlamanız gerekiyor.
# /etc/nginx/nginx.conf
http {
# Genel API rate limiting zone'u
# 10mb bellek = yaklaşık 160.000 IP adresi tutabilir
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
# Login sayfası için daha kısıtlı zone
limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;
# Genel web trafiği için zone
limit_req_zone $binary_remote_addr zone=general_limit:10m rate=30r/s;
# Logları yapılandır
limit_req_log_level warn;
limit_req_status 429;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
Burada $binary_remote_addr kullanmamızın sebebi, $remote_addr‘a göre daha az bellek tüketmesidir. IPv4 için 4 byte, IPv6 için 16 byte yer kaplar.
Login Sayfası Koruması
Login sayfası koruması en kritik kullanım senaryosudur. Dakikada 5 istek oldukça katı görünebilir ama meşru kullanıcılar için bu yeterli fazlasıyla yeterli, brute force saldırıları için ise geçilemez bir bariyer oluşturur.
# /etc/nginx/sites-available/myapp.conf
server {
listen 80;
server_name myapp.example.com;
# Login endpoint'i için özel rate limiting
location /auth/login {
limit_req zone=login_limit burst=3 nodelay;
limit_req_status 429;
# Rate limit aşıldığında özel sayfa göster
error_page 429 /rate_limit.html;
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
# Şifre sıfırlama da kritik bir endpoint
location /auth/forgot-password {
limit_req zone=login_limit burst=2 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Real-IP $remote_addr;
}
# Rate limit sayfası
location = /rate_limit.html {
root /var/www/html;
internal;
}
# Genel uygulama trafiği
location / {
limit_req zone=general_limit burst=20 nodelay;
proxy_pass http://127.0.0.1:3000;
}
}
Özel hata sayfası oluşturabilirsiniz:
# /var/www/html/rate_limit.html oluştur
cat > /var/www/html/rate_limit.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Çok Fazla İstek</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
h1 { color: #e74c3c; }
</style>
</head>
<body>
<h1>429 - Çok Fazla İstek</h1>
<p>Kısa sürede çok fazla istek gönderdiniz. Lütfen bekleyin ve tekrar deneyin.</p>
</body>
</html>
EOF
API Rate Limiting Yapılandırması
API’ler için genellikle daha granüler bir yapılandırma gerekir. Farklı endpoint’ler farklı limitlere ihtiyaç duyabilir.
# /etc/nginx/sites-available/api.conf
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
# Varsayılan API rate limit
limit_req zone=api_limit burst=20 nodelay;
# Arama endpoint'i - daha kısıtlı
location /v1/search {
limit_req zone=api_limit burst=5 nodelay;
limit_req_status 429;
add_header X-RateLimit-Limit 10;
add_header X-RateLimit-Remaining $upstream_http_x_ratelimit_remaining;
proxy_pass http://backend_api;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Authorization $http_authorization;
}
# Dosya upload endpoint'i - çok daha kısıtlı
location /v1/upload {
limit_req zone=login_limit burst=2 nodelay;
limit_req_status 429;
client_max_body_size 50m;
proxy_pass http://backend_api;
proxy_read_timeout 120s;
}
# Genel API endpoint'leri
location /v1/ {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend_api;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Health check endpoint'i - limit yok
location /health {
access_log off;
proxy_pass http://backend_api;
}
}
upstream backend_api {
server 127.0.0.1:8080;
keepalive 32;
}
API Key Bazlı Rate Limiting
IP bazlı rate limiting her zaman yeterli değildir. Özellikle CDN arkasında çalışan veya birden fazla kullanıcının aynı IP’yi paylaştığı durumlarda API key bazlı rate limiting daha adil bir çözüm sunar.
# /etc/nginx/nginx.conf - http bloğu içine ekle
http {
# API key'e göre rate limiting
# $http_x_api_key header'ından API key'i al
limit_req_zone $http_x_api_key zone=api_key_limit:10m rate=100r/s;
# Hem IP hem API key kombinasyonu
# Bu sayede aynı API key farklı IP'lerden gelirse de sınırlanır
map $http_x_api_key $rate_limit_key {
default $binary_remote_addr;
"~^[A-Za-z0-9]{32,}$" $http_x_api_key;
}
limit_req_zone $rate_limit_key zone=smart_limit:10m rate=50r/s;
}
# API key bazlı limit kullanan server bloğu
server {
listen 443 ssl;
server_name api.example.com;
location /v2/ {
# API key varsa o key'e göre, yoksa IP'ye göre limitler
limit_req zone=smart_limit burst=100 nodelay;
limit_req_status 429;
# API key yoksa 401 döndür
if ($http_x_api_key = "") {
return 401 '{"error": "API key gerekli"}';
}
add_header Content-Type application/json;
proxy_pass http://backend_api;
}
}
Whitelist Oluşturma
Bazı IP adreslerini (örneğin kendi ofis IP’niz, monitoring sunucunuz) rate limiting dışında tutmak isteyebilirsiniz.
# /etc/nginx/conf.d/whitelist.conf
geo $limit {
default 1;
# Ofis IP'si - rate limit yok
203.0.113.0/24 0;
# Monitoring sunucusu
198.51.100.5 0;
# Localhost
127.0.0.1 0;
10.0.0.0/8 0;
# Staging sunucusu
192.168.1.0/24 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
# Zone tanımlaması whitelist destekli
# limit_req_zone $limit_key zone=api_limit_wl:10m rate=10r/s;
Bu yapılandırmada $limit_key boş string olduğunda Nginx rate limiting uygulamaz. Bu zarif bir whitelist mekanizması sağlar.
Log Analizi ve Monitoring
Rate limiting ne kadar etkili çalışıyor, hangi IP’ler engelleniyor, bunları takip etmek kritik önem taşıyor.
# Nginx log formatını rate limit bilgileri içerecek şekilde özelleştir
# /etc/nginx/nginx.conf - http bloğu içine
log_format rate_limit_log '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time '
'limit_req=$limit_req_status';
access_log /var/log/nginx/access.log rate_limit_log;
Rate limit ihlallerini gerçek zamanlı takip etmek için basit bir script yazabilirsiniz:
#!/bin/bash
# /usr/local/bin/rate_limit_monitor.sh
# Rate limit ihlallerini analiz et ve raporla
LOG_FILE="/var/log/nginx/error.log"
THRESHOLD=100 # Eşik değer - bu kadar ihlal görülen IP'leri raporla
echo "=== Rate Limit İhlal Raporu - $(date) ==="
echo ""
# 429 durumunu loglardan çek
echo "Son 1 saatteki rate limit ihlalleri:"
grep "limiting requests" $LOG_FILE |
grep "$(date -d '1 hour ago' '+%Y/%m/%d %H')" |
awk '{print $NF}' |
grep -oP 'd+.d+.d+.d+' |
sort | uniq -c | sort -rn |
head -20
echo ""
echo "En çok engellenen IP'ler (son 24 saat):"
grep "limiting requests" $LOG_FILE |
grep "$(date '+%Y/%m/%d')" |
grep -oP 'd+.d+.d+.d+' |
sort | uniq -c | sort -rn |
head -10 | while read count ip; do
echo " $ip : $count kez engellendi"
# IP hakkında ek bilgi al
# whois $ip | grep -i "orgname|country" | head -2
done
# Kritik eşiği aşan IP'leri bildir
echo ""
echo "KRİTİK: Eşiği ($THRESHOLD) aşan IP'ler:"
grep "limiting requests" $LOG_FILE |
grep -oP 'd+.d+.d+.d+' |
sort | uniq -c | sort -rn |
awk -v threshold="$THRESHOLD" '$1 > threshold {print " ENGELLE: "$2" ("$1" ihlal)"}'
# Script'i çalıştırılabilir yap ve cron'a ekle
chmod +x /usr/local/bin/rate_limit_monitor.sh
# Her saat başı çalıştır
echo "0 * * * * root /usr/local/bin/rate_limit_monitor.sh >> /var/log/rate_limit_report.log 2>&1" >> /etc/cron.d/nginx-monitor
Konfigürasyonu Test Etme
Yapılandırmayı uygulamadan önce test etmek ve sonuçları doğrulamak şart.
# Nginx konfigürasyonunu sözdizimi kontrolü
nginx -t
# Başarılı çıktı şöyle görünmeli:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration is successful
# Konfigürasyonu yeniden yükle (servis kesintisi olmadan)
systemctl reload nginx
# Rate limiting'i test et - Apache Benchmark ile
# Login endpoint'ine 50 eşzamanlı bağlantıyla 200 istek gönder
ab -n 200 -c 50 -T 'application/json'
-p /tmp/login.json
https://myapp.example.com/auth/login
# /tmp/login.json dosyası
echo '{"username":"test","password":"test123"}' > /tmp/login.json
# curl ile manuel test
for i in {1..10}; do
response=$(curl -s -o /dev/null -w "%{http_code}"
-X POST
-H "Content-Type: application/json"
-d '{"username":"test","password":"wrongpass"}'
https://myapp.example.com/auth/login)
echo "İstek $i: HTTP $response"
sleep 0.1
done
Gerçek Dünya Senaryosu: E-Ticaret Sitesi
Şimdi gerçek bir e-ticaret sitesi için kapsamlı bir konfigürasyon oluşturalım:
# /etc/nginx/nginx.conf - http bloğu
http {
# Farklı kullanım senaryoları için zone'lar
limit_req_zone $binary_remote_addr zone=shop_general:10m rate=20r/s;
limit_req_zone $binary_remote_addr zone=shop_login:10m rate=3r/m;
limit_req_zone $binary_remote_addr zone=shop_api:10m rate=30r/s;
limit_req_zone $binary_remote_addr zone=shop_checkout:10m rate=10r/m;
limit_req_zone $binary_remote_addr zone=shop_search:10m rate=5r/s;
# Bant genişliği limiti de ekle
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_status 429;
limit_req_log_level warn;
}
# /etc/nginx/sites-available/shop.conf
server {
listen 443 ssl http2;
server_name shop.example.com;
# Genel bağlantı limiti - bir IP'den max 20 eşzamanlı bağlantı
limit_conn addr 20;
# Statik dosyalar - rate limit yok, cache ile servis et
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
root /var/www/shop/public;
}
# Login ve kayıt
location ~ ^/account/(login|register|forgot-password) {
limit_req zone=shop_login burst=2 nodelay;
proxy_pass http://shop_backend;
proxy_set_header X-Real-IP $remote_addr;
}
# Ödeme sayfaları - son derece kısıtlı
location ~ ^/(checkout|payment) {
limit_req zone=shop_checkout burst=3 nodelay;
proxy_pass http://shop_backend;
proxy_set_header X-Real-IP $remote_addr;
# SSL doğrulaması için ek header
proxy_set_header X-Forwarded-Proto $scheme;
}
# Ürün arama
location /search {
limit_req zone=shop_search burst=10 nodelay;
proxy_pass http://shop_backend;
proxy_cache_valid 200 1m;
}
# Genel sayfa trafiği
location / {
limit_req zone=shop_general burst=30 nodelay;
proxy_pass http://shop_backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
upstream shop_backend {
server 127.0.0.1:8080 weight=3;
server 127.0.0.1:8081 weight=1 backup;
keepalive 64;
}
Yaygın Hatalar ve Çözümleri
Rate limiting konfigürasyonunda sık yapılan hatalar ve bunlardan nasıl kaçınılacağı:
- Çok katı limitler: Meşru kullanıcıları engellemek müşteri kaybına yol açar. Önce logları analiz edin, gerçek kullanım örüntülerini belirleyin, sonra limit koyun.
- Reverse proxy arkasında yanlış IP: Eğer Nginx başka bir load balancer’ın arkasındaysa
$remote_addrher zaman load balancer’ın IP’si olur. Bu durumdaset_real_ip_fromdirektifini kullanın:
# /etc/nginx/conf.d/real_ip.conf
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
- Zone boyutunu küçük tutmak: Yüksek trafikli sitelerde zone dolduğunda Nginx en eski girişleri siler. 16.000 IP adresi için yaklaşık 1MB yeterli, ama büyük siteler için 50m veya daha fazlası düşünülmeli.
- burst değerini unutmak:
burstdeğeri olmadan anlık yoğunluklar bile meşru kullanıcıları etkiler. Özellikle mobil uygulamalar, sayfa yüklenirken paralel API çağrıları yapar.
- Hata mesajında bilgi sızdırmak: 429 yanıtında sunucu teknolojinizi veya iç yapınızı açıklayan bilgiler paylaşmayın.
Fail2ban ile Entegrasyon
Rate limiting geçici bir yavaşlatma sağlar, kalıcı engelleme için Fail2ban ile kombinlemek mantıklıdır.
# /etc/fail2ban/filter.d/nginx-rate-limit.conf
[Definition]
failregex = ^<HOST> .* "(GET|POST|PUT|DELETE|HEAD).*HTTP.*" 429
ignoreregex =
# /etc/fail2ban/jail.d/nginx-rate-limit.conf
[nginx-rate-limit]
enabled = true
port = http,https
filter = nginx-rate-limit
logpath = /var/log/nginx/access.log
maxretry = 10
findtime = 600
bantime = 3600
action = iptables-multiport[name=nginx-rate-limit, port="http,https", protocol=tcp]
# Fail2ban servisini yeniden başlat
systemctl restart fail2ban
# Durumu kontrol et
fail2ban-client status nginx-rate-limit
Bu kombinasyon son derece güçlü bir savunma katmanı oluşturur. Nginx kısa vadede yavaşlatır, Fail2ban ise ısrarcı saldırganları firewall seviyesinde engeller.
Sonuç
Nginx rate limiting, doğru yapılandırıldığında oldukça az kaynak kullanarak çok büyük bir güvenlik değeri sağlar. Login sayfalarınızı brute force saldırılarına karşı korumak, API’lerinizi aşırı kullanıma karşı şiellemek ve genel sunucu stabilitesini artırmak için bu özellikleri mutlaka kullanmalısınız.
Uygulamayı adım adım yapmanızı öneririm: önce sadece login endpoint’ine başlayın, logları izleyin, sonra API endpoint’lerini ekleyin. Her değişikliği nginx -t ile test edin ve ab ya da curl ile doğrulayın. Whitelist’inizi hazır tutun, monitoring scriptinizi cron’a ekleyin ve Fail2ban ile entegre edin.
Unutmayın, güvenlik tek bir katmandan oluşmaz. Rate limiting, WAF, SSL/TLS yapılandırması, güvenli header’lar ve uygulama seviyesi güvenliği bir arada çalışmalıdır. Ama başlangıç noktası olarak rate limiting, en hızlı uygulanabilen ve en somut sonuç veren önlemlerden biridir.