Bir web sunucusu yonetirken en sık karşılaşılan sorunların başında tek bir IP adresinden gelen aşırı bağlantı sayısı gelir. Birisi sitenizi kazımaya çalışıyor, bir bot saldırısı oluyor ya da iyi niyetle tasarlanmış bir script yanlışlıkla sunucunuzu bunaltıyor olabilir. Nginx’in limit_conn modülü, bu tür durumlar için sade ama etkili bir çözüm sunar. Bu yazıda limit_conn direktifini gerçek dünya senaryolarıyla birlikte ele alacağız.
limit_conn Nedir ve Neden Lazım?
limit_conn modülü, belirli bir anahtar değerine göre eş zamanlı bağlantı sayısını sınırlamanıza izin verir. Bu anahtar genellikle istemcinin IP adresi olur, ama sunucu adı veya başka bir değişken de olabilir.
limit_rate veya limit_req ile karıştırmamak gerekir. Bunların her biri farklı şeyleri kontrol eder:
- limit_conn: Aynı anda kaç bağlantı açık tutulabilir
- limit_req: Saniyede kaç istek gelebilir (rate limiting)
- limit_rate: Bağlantı başına bant genişliği limiti
Diyelim ki bir kullanıcı sitenizden video izliyor ve bağlantısını açık tutuyor. Aynı zamanda 50 farklı sekme daha açarsa, limit_conn devreye girer ve 51. bağlantıyı reddeder. Bu özellikle uzun süreli bağlantıların söz konusu olduğu download servisleri, streaming uygulamaları ve API gateway’leri için kritik öneme sahiptir.
Temel Yapılandırma
Nginx’te limit_conn kullanmak iki adımdan oluşur. Önce bir paylaşımlı bellek alanı tanımlarsınız, sonra bu alana bir limit atarsınız.
# /etc/nginx/nginx.conf - http bloğu içine
http {
# Paylaşımlı bellek alanı tanımla
# "addr" ismi, $binary_remote_addr anahtarı, 10m boyut
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name example.com;
location / {
# Bu zone için maksimum 10 eş zamanlı bağlantı
limit_conn addr 10;
}
}
}
Burada birkaç önemli nokta var. $binary_remote_addr değişkeni, $remote_addr‘a göre daha az bellek kullanır. IPv4 için 4 byte, IPv6 için 16 byte tüketir. 10 megabyte’lık bir zone yaklaşık 160.000 IPv4 adresini tutabilir; bu çoğu senaryo için fazlasıyla yeterlidir.
Zone adı (addr burada) tamamen size kalmış. Anlamlı isimler kullanmak yapılandırma dosyasını daha okunabilir kılar.
limit_conn_zone Parametrelerini Anlamak
limit_conn_zone direktifini doğru yapılandırmak için parametrelerini iyi anlamak gerekir.
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:5m;
- $binary_remote_addr: İstemci IP adresini binary formatta saklar, bellek verimlidir
- $remote_addr: IP adresini string olarak saklar, daha fazla yer kaplar
- $server_name: Sanal host bazlı sınırlama için kullanılır
- zone=isim:boyut: Zone ismi ve ayrılan bellek miktarı
Zone boyutunu belirlerken şunu göz önünde bulundurun: 1 megabyte yaklaşık 8.000 durum kaydı tutabilir (64-bit sistemlerde). Yüksek trafikli bir sunucuda daha büyük zone’lar tanımlamak mantıklıdır.
Gerçek Dünya Senaryo 1: Download Sunucusu
Bir dosya paylaşım servisi işlettiğinizi düşünün. Tek bir kullanıcının sunucuyu tüm bant genişliğiyle ele geçirmesini istemiyorsunuz. Hem bağlantı sayısını hem de download hızını sınırlamak istiyorsunuz.
http {
limit_conn_zone $binary_remote_addr zone=download_zone:20m;
server {
listen 80;
server_name files.example.com;
location /downloads/ {
limit_conn download_zone 3;
limit_rate 500k; # Bağlantı başına 500KB/s
limit_rate_after 1m; # İlk 1MB sonra hız sınırla
root /var/www/files;
autoindex on;
}
}
}
Bu yapılandırmayla tek bir IP adresi aynı anda en fazla 3 dosya indirebilir ve her bağlantı 500KB/s ile sınırlandırılır. İlk megabyte tam hızda gelir, sonrasında throttling başlar. Kullanıcı deneyimini bozmadan kaynakları korumak için iyi bir denge.
Gerçek Dünya Senaryo 2: API Gateway
API’larınızı dış dünyaya açıyorsanız, istemcilerin eş zamanlı bağlantı sayısını kontrol altında tutmak önemlidir. Özellikle authentication endpoint’leri brute force saldırılarına karşı savunmasız olabilir.
http {
limit_conn_zone $binary_remote_addr zone=api_conn:10m;
limit_req_zone $binary_remote_addr zone=api_req:10m rate=30r/m;
server {
listen 443 ssl;
server_name api.example.com;
# Genel API bağlantı limiti
limit_conn api_conn 20;
location /api/v1/ {
proxy_pass http://backend_servers;
proxy_set_header X-Real-IP $remote_addr;
}
# Authentication endpoint - daha sıkı limit
location /api/v1/auth/ {
limit_conn api_conn 5;
limit_req zone=api_req burst=10 nodelay;
proxy_pass http://backend_servers;
}
}
}
Dikkat edin: location bloğunda tanımlanan limit_conn direktifi, server bloğundaki genel limiti ezer. /api/v1/auth/ için bağlantı limiti 5 olurken, diğer endpoint’ler için 20 kalır.
Hata Kodlarını Özelleştirme
Varsayılan olarak limit_conn limiti aştığında Nginx 503 Service Unavailable döner. Bunu değiştirebilirsiniz.
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
server_name example.com;
location / {
limit_conn addr 10;
limit_conn_status 429; # Too Many Requests
# Özel hata sayfası
error_page 429 /errors/429.html;
}
location = /errors/429.html {
root /var/www;
internal;
}
}
}
429 Too Many Requests kullanmak semantik olarak daha doğrudur ve istemcinin durumu daha iyi anlamasını sağlar. Özellikle API’larla çalışırken istemcilerin hata kodlarına göre davranış sergilemesi beklenir.
Log Seviyesini Ayarlama
Varsayılan log seviyesi error‘dür. Yoğun trafikli sunucularda bu log dosyalarını çok hızlı büyütebilir. limit_conn_log_level ile bunu kontrol edebilirsiniz.
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
listen 80;
location / {
limit_conn addr 10;
limit_conn_log_level warn; # error, warn, notice, info
}
}
}
- error: Her sınır aşımı hata olarak loglanır (varsayılan)
- warn: Daha az ciddi, uyarı olarak loglanır
- notice: Bilgilendirme amaçlı
- info: En az ayrıntılı
Prodüksiyon ortamında warn genellikle iyi bir seçimdir. Saldırıları fark edebilirsiniz ama normal kullanım sırasındaki geçici aşımlar log dosyanızı doldurmaz.
Birden Fazla Zone Kullanmak
Tek bir location bloğuna birden fazla limit_conn direktifi ekleyebilirsiniz. Her direktif farklı bir zone’a referans verir.
http {
# IP bazlı zone
limit_conn_zone $binary_remote_addr zone=perip:10m;
# Sunucu bazlı zone
limit_conn_zone $server_name zone=perserver:5m;
server {
listen 80;
server_name example.com;
location / {
# Hem IP bazlı hem de sunucu bazlı limit
limit_conn perip 10;
limit_conn perserver 100;
}
}
}
Bu yapılandırmayla tek bir IP adresi en fazla 10 bağlantı açabilirken, example.com sunucusu toplam 100 bağlantı alabilir. Eğer herhangi bir limit aşılırsa bağlantı reddedilir.
Bu pattern özellikle shared hosting ortamlarında kullanışlıdır. Her sanal host için ayrı bir server_name zone’u tanımlayarak bir müşterinin diğerlerini etkilemesini önleyebilirsiniz.
Gerçek Dünya Senaryo 3: WordPress Koruması
WordPress siteleri xmlrpc.php ve wp-login.php gibi endpoint’ler üzerinden sıkça saldırıya uğrar. Bu endpoint’ler için özel limitler tanımlamak site güvenliğini artırır.
http {
limit_conn_zone $binary_remote_addr zone=wp_zone:10m;
limit_req_zone $binary_remote_addr zone=wp_login:10m rate=5r/m;
server {
listen 80;
server_name myblog.com www.myblog.com;
root /var/www/wordpress;
index index.php;
# Genel bağlantı limiti
limit_conn wp_zone 15;
# wp-login.php için agresif limit
location = /wp-login.php {
limit_conn wp_zone 3;
limit_req zone=wp_login burst=5 nodelay;
limit_conn_status 429;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# xmlrpc.php'yi tamamen engelle
location = /xmlrpc.php {
deny all;
return 444;
}
location ~ .php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
return 444 Nginx’e özgü bir kod olup bağlantıyı yanıt göndermeden kapatır. Botlar için en etkili yanıt budur, çünkü ne 200 ne de bir hata kodu alırlar.
Yapılandırmayı Test Etme
Her değişiklikten sonra yapılandırmayı test etmek kritiktir. Hatalı bir Nginx konfigürasyonu tüm sitenizi çökertebilir.
# Yapılandırma dosyasını test et
sudo nginx -t
# Başarılı çıktı şöyle görünmeli:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# Nginx'i yeniden yükle (sıfırdan başlatmadan)
sudo nginx -s reload
# Ya da systemd ile
sudo systemctl reload nginx
nginx -t komutu yapılandırmayı hafızaya yükler ve syntax hatalarını raporlar. reload ise mevcut bağlantıları kesmeden yeni yapılandırmayı devreye alır. Prodüksiyon sunucularında her zaman restart yerine reload kullanın.
Limit_conn Durumunu İzleme
ngx_http_stub_status_module modülü aktif bağlantı sayısı hakkında temel bilgi verir.
# nginx.conf içine
server {
listen 80;
server_name example.com;
location /nginx_status {
stub_status on;
access_log off;
# Sadece yerel erişime izin ver
allow 127.0.0.1;
allow 192.168.1.0/24;
deny all;
}
}
Status sayfasına eriştiğinizde şuna benzer bir çıktı görürsünüz:
curl http://localhost/nginx_status
# Çıktı:
# Active connections: 43
# server accepts handled requests
# 1234 1234 5678
# Reading: 0 Writing: 12 Waiting: 31
- Active connections: Şu an açık bağlantı sayısı
- Reading: İstek header’ı okunan bağlantılar
- Writing: Yanıt yazılan bağlantılar
- Waiting: Keep-alive ile bekleyen bağlantılar
Daha ayrıntılı metrikler için Nginx Plus’ın status modülünü veya üçüncü parti araçları (nginx-lua, OpenResty) değerlendirin.
Log Analizi ile Limit Aşımlarını Takip Etme
Limit_conn tarafından reddedilen bağlantıları log’lardan takip edebilirsiniz.
# Hata loglarında limit_conn kayıtlarını ara
sudo grep "limiting connections" /var/log/nginx/error.log | tail -20
# IP bazında gruplandır
sudo grep "limiting connections" /var/log/nginx/error.log |
awk '{print $NF}' |
sort | uniq -c |
sort -rn | head -10
# Son 1 saatteki limit aşımları
sudo awk -v d="$(date -d '1 hour ago' '+%Y/%m/%d %H:%M')"
'$0 >= d' /var/log/nginx/error.log |
grep "limiting connections" | wc -l
Bu loglar hangi IP’lerin limit aşımı yaptığını, hangi saatlerde yoğunluğun arttığını ve limit değerlerinizi fine-tune etmeniz gerekip gerekmediğini anlamanıza yardımcı olur.
Reverse Proxy Arkasında Çalışırken Dikkat Edilecekler
Nginx bir load balancer veya CDN arkasında çalışıyorsa $remote_addr her zaman gerçek istemci IP’sini vermez. Proxy’nin IP’sini verir.
http {
# Gerçek IP'yi al
set_real_ip_from 192.168.1.0/24; # Load balancer subnet'i
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# Artık $remote_addr gerçek istemci IP'sini içerir
limit_conn_zone $remote_addr zone=realip_zone:10m;
server {
listen 80;
location / {
limit_conn realip_zone 10;
}
}
}
real_ip_recursive on ayarı, X-Forwarded-For header’ında birden fazla IP varsa bilinen proxy IP’lerini atarak gerçek istemci IP’sine ulaşır. Cloudflare, AWS CloudFront gibi CDN’lerin IP bloklarını mutlaka set_real_ip_from direktifine eklemelisiniz.
limit_conn ve limit_req’yi Birlikte Kullanmak
Gerçek dünya uygulamalarında genellikle her ikisi de birlikte kullanılır. limit_conn eş zamanlı bağlantıları, limit_req ise istek hızını kontrol eder.
http {
limit_conn_zone $binary_remote_addr zone=conn_zone:10m;
limit_req_zone $binary_remote_addr zone=req_zone:10m rate=100r/s;
server {
listen 80;
server_name example.com;
location / {
# Eş zamanlı bağlantı limiti
limit_conn conn_zone 20;
# İstek hızı limiti
limit_req zone=req_zone burst=50 nodelay;
proxy_pass http://backend;
}
}
}
Bu ikili koruma farklı saldırı vektörlerini ele alır. Birisi bağlantıları açık tutarak kaynak tüketmeye çalışabilir (slowloris gibi), ya da hızlı bağlantı kurarak flood saldırısı yapabilir. Her iki direktifin varlığı bu senaryoları birbirinden bağımsız olarak ele alır.
Performans Etkileri ve Boyutlandırma
limit_conn_zone için bellek boyutunu doğru ayarlamak önemlidir. Zone dolduğunda Nginx yeni bağlantılar için yer açmaya çalışır. Eski ve az kullanılan kayıtları temizler, ama zone çok küçükse bu bile yetmeyebilir.
# 1 megabyte'ın karşılığı yaklaşık kayıt sayısı
# IPv4 (4 byte): ~16.000 kayıt
# IPv6 (16 byte): ~8.000 kayıt
# Yüksek trafikli site için önerilen değerler
limit_conn_zone $binary_remote_addr zone=main:50m;
# Kritik endpoint için ayrı küçük zone
limit_conn_zone $binary_remote_addr zone=auth:5m;
Zone boyutunu belirlerken eş zamanlı aktif benzersiz IP sayısını tahmin edin. Günde 100.000 ziyaretçi alan bir site için bile 50MB genellikle yeterlidir, çünkü tüm ziyaretçiler aynı anda aktif değildir.
Beyaz Liste Oluşturmak
Bazı IP’leri limitlerden muaf tutmak isteyebilirsiniz. Monitoring araçlarınız, iç ağınız veya güvenilir partnerler için bu gerekli olabilir.
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
geo $limit {
default 1;
127.0.0.1 0; # Localhost
192.168.1.0/24 0; # İç ağ
10.10.10.5 0; # Monitoring sunucusu
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_conn_zone $limit_key zone=addr:10m;
server {
listen 80;
location / {
limit_conn addr 10;
}
}
}
geo bloğu ile beyaz listedeki IP’ler için $limit değişkeni 0 olur. map bloğu bu değeri $limit_key değişkenine çevirir: beyaz listedeki IP’ler için boş string, diğerleri için IP adresi. limit_conn_zone boş string’i anahtar olarak kullanamayacağından bu IP’ler için hiçbir kayıt tutulmaz ve limitler uygulanmaz.
Sonuç
Nginx limit_conn modülü, web sunucunuzu eş zamanlı bağlantı saldırılarına karşı korumak için minimal konfigürasyonla maksimum etki sağlayan bir araçtır. Temel kullanımı birkaç satıra inse de ince ayarlar ve kombinasyonlar ciddi bir güvenlik katmanı oluşturur.
Önemli noktalara bakacak olursak:
- Her senaryo için ayrı zone’lar tanımlamak yönetimi kolaylaştırır
$binary_remote_addrkullanımı bellek verimliliğini artırırlimit_connvelimit_reqbirlikte kullanıldığında daha kapsamlı koruma sağlar- Reverse proxy arkasında gerçek IP tespiti için
real_ipmodülünü mutlaka yapılandırın - Zone boyutlarını trafik tahminlerinize göre belirleyin
- Log seviyesini ortamınıza göre ayarlayın
Başlangıç için her IP’ye 10-20 eş zamanlı bağlantı makul bir değerdir. Sonra loglara bakarak ve gerçek kullanım örüntülerini inceleyerek bu değeri revize edin. Sıkı bir limitten başlayıp gevşetmek, gevşek başlayıp sıkıştırmaya çalışmaktan her zaman daha güvenlidir.