Yeni bir Debian sunucusu kurduğunda önüne gelen ilk görevlerden biri genellikle bir web sunucusu ayağa kaldırmak olur. Apache mi, Nginx mi tartışması eski bir tartışma ama minimal kaynak kullanımı ve yüksek performans söz konusu olduğunda Nginx çoğunlukla kazanıyor. Bu yazıda sıfırdan başlayarak Debian üzerinde tam anlamıyla çalışan, güvenli ve production’a hazır bir Nginx kurulumu yapacağız. Gereksiz paketleri sistemden uzak tutacak, sadece ihtiyacımız olanı kuracağız.
Neden Minimal Kurulum?
Çoğu sysadmin “apt install nginx” yazıp geçer. Sunucu ayağa kalkar, her şey çalışır gibi görünür. Ama altı ay sonra o sunucuda kim ne kurmuş, hangi modüller aktif, hangi konfigürasyon dosyası neyi eziyor belli olmaz. Minimal kurulum yaklaşımı sadece “az paket kur” demek değil. Neyin kurulduğunu bilmek, neyin çalıştığını anlamak ve ileride sorun çıktığında nereye bakacağını bilmek demek.
Bunun yanında minimal kurulum güvenlik açısından da kritik. Kurulu olmayan bir servisi exploit etmek mümkün değil. Her ekstra modül, her ekstra paket potansiyel bir saldırı yüzeyi.
Sistemi Hazırlamak
Başlamadan önce sistemin güncel olduğundan emin olmak gerekiyor. Debian’da bu adımı atlayan sunucularla sonradan çok kötü deneyimler yaşadım.
apt update && apt upgrade -y
apt autoremove -y
Şimdi sunucunun kim olduğunu öğrenelim:
hostnamectl
uname -r
cat /etc/debian_version
Bu komutların çıktısı sana Debian sürümünü, kernel versiyonunu ve hostname bilgisini verir. Özellikle eski Debian sürümlerinde repo’lar farklı olabiliyor, bu yüzden sürümü bilmek önemli.
Temel araçları kuralım. Bunlar olmadan iş yapmak zorlaşıyor:
apt install -y curl wget gnupg2 ca-certificates lsb-release apt-transport-https
Nginx Kurulumu: Official Repo mu, Debian Repo mu?
Burada kritik bir karar var. Debian’ın kendi reposundaki Nginx sürümü genellikle birkaç major sürüm geride kalıyor. Örneğin Debian 12 (Bookworm) ile gelen Nginx, upstream’deki stable sürümünün oldukça gerisinde olabiliyor. Production ortamında güvenlik yamaları açısından bu risk oluşturabilir.
Nginx’in official reposunu ekleyerek güncel sürümü kullanmak daha sağlıklı:
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor
| tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg]
http://nginx.org/packages/debian $(lsb_release -cs) nginx"
| tee /etc/apt/sources.list.d/nginx.list
apt update
apt install -y nginx
Kurulumun başarılı olup olmadığını kontrol edelim:
nginx -v
systemctl status nginx
Eğer servis henüz başlamamışsa:
systemctl start nginx
systemctl enable nginx
systemctl enable komutu nginx’i sistem her açıldığında otomatik başlatacak şekilde ayarlıyor. Production sunucularda bunu unutmak ilerleyen günlerde bir sunucu yeniden başladığında seni güzel bir sürprizle karşılaştırır.
Nginx’in Dizin Yapısını Anlamak
Nginx kurulumunun ardından dosyaların nerede olduğunu bilmek gerekiyor. Çoğu zaman insanlar konfigürasyon dosyasını arayıp durur.
Ana konfigürasyon dosyası /etc/nginx/nginx.conf içinde. Ama bütün işi tek bir dosyada yapmak yerine modüler yapıyı kullanmak çok daha akıllıca.
- /etc/nginx/nginx.conf: Ana konfigürasyon dosyası
- /etc/nginx/conf.d/: Ek konfigürasyon dosyaları buraya gidiyor
- /etc/nginx/sites-available/: Virtual host tanımları (bazı kurulumlarda bu dizin olmayabilir)
- /var/log/nginx/: Access ve error logları
- /var/www/html/: Varsayılan web root dizini
- /usr/share/nginx/html/: Nginx’in kendi örnek sayfaları
Official Nginx paketinde sites-available/sites-enabled yapısı gelmiyor, bu Debian’ın kendi yapılandırması. Official pakette conf.d/ dizini kullanılıyor. Her iki yaklaşım da çalışır ama karıştırmamak lazım.
nginx.conf’u Düzenlemek
Varsayılan nginx.conf dosyası iş görür ama optimize edilmemiş. Şu ana konfigürasyona bakalım:
cat /etc/nginx/nginx.conf
Şimdi bunu production için daha uygun hale getirelim. Bu konfigürasyonu kendi sunucuma özgü ihtiyaçlara göre yıllarca geliştirdim, buradaki versiyon başlangıç için sağlam bir temel:
cat > /etc/nginx/nginx.conf << 'EOF'
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml application/json
application/javascript application/rss+xml
application/atom+xml image/svg+xml;
include /etc/nginx/conf.d/*.conf;
}
EOF
Burada önemli birkaç satırı açıklayayım:
- worker_processes auto: CPU çekirdek sayısına göre otomatik worker process ayarlar
- use epoll: Linux’ta en verimli I/O event mekanizması
- server_tokens off: Nginx versiyon bilgisini response header’larından gizler
- gzip on: Yanıtları sıkıştırarak bant genişliği tasarrufu sağlar
İlk Virtual Host Konfigürasyonu
Şimdi gerçek bir site için virtual host oluşturalım. Diyelim ki example.com için bir statik site sunuyoruz:
mkdir -p /var/www/example.com/html
chown -R nginx:nginx /var/www/example.com
chmod -R 755 /var/www/example.com
# Test sayfası oluştur
cat > /var/www/example.com/html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
<title>Merhaba Dünya</title>
</head>
<body>
<h1>Nginx Çalışıyor!</h1>
</body>
</html>
EOF
Şimdi virtual host konfigürasyonunu oluşturalım:
cat > /etc/nginx/conf.d/example.com.conf << 'EOF'
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
access_log /var/log/nginx/example.com_access.log main;
error_log /var/log/nginx/example.com_error.log warn;
location / {
try_files $uri $uri/ =404;
}
location ~* .(jpg|jpeg|png|gif|ico|css|js|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
location ~ /. {
deny all;
}
}
EOF
Konfigürasyonu test edip yeniden yükleyelim:
nginx -t
systemctl reload nginx
nginx -t komutu syntax hatalarını kontrol eder. Canlı ortamda reload yapmadan önce bu komutu çalıştırmayı hiç atlamıyorum. Bir keresinde atladım, siteyi 20 dakika çevrimdışı bıraktım. O günden beri atlamıyorum.
Güvenlik Duvarı Ayarları
Nginx kurulumu bitti ama dışarıdan erişim için firewall’u ayarlamak gerekiyor. Debian’da ufw kullanıyorum, hem basit hem de yeterince güçlü:
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verbose
Eğer SSH port’unu değiştirdiysen 22 yerine kendi port’unu yaz. UFW enable yaparken mevcut SSH bağlantısını kesmez ama yeni bağlantı açamama riskini göz önünde bulundur.
SSL/TLS ile HTTPS Kurulumu
2024’te HTTP üzerinden servis sunmak artık kabul edilebilir değil. Let’s Encrypt ile ücretsiz SSL sertifikası almak hem kolay hem de otomatik yenileniyor. Certbot kurulumu:
apt install -y certbot python3-certbot-nginx
Sertifika almak:
certbot --nginx -d example.com -d www.example.com
Certbot Nginx konfigürasyonunu otomatik olarak güncelliyor. Ama ben otomatik güncelleme yerine manuel kontrolü tercih ediyorum. Certbot’un ne yaptığını görmek için:
cat /etc/nginx/conf.d/example.com.conf
Otomatik yenilemeyi test edelim:
certbot renew --dry-run
Certbot sisteme bir cron job veya systemd timer ekliyor. Bunu doğrulamak için:
systemctl list-timers | grep certbot
Gelişmiş Güvenlik Başlıkları
Temel kurulum tamam ama güvenlik başlıklarını da eklemek gerekiyor. Bu başlıklar XSS, clickjacking ve diğer client-side saldırılara karşı koruma sağlıyor. Virtual host konfigürasyonuna SSL bloğu içinde şunları ekle:
cat >> /etc/nginx/conf.d/example.com.conf << 'EOF'
# Guvenlik basliklari icin ayri bir dosya daha temiz olur
EOF
cat > /etc/nginx/conf.d/security-headers.conf << 'EOF'
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
EOF
Bu başlıkları http bloğu yerine ayrı bir dosyaya koyup include etmek, birden fazla virtual host’ta tekrar kullanmayı kolaylaştırıyor.
Log Yönetimi
Production ortamında logları yönetmek kritik. Nginx logları hızla büyüyebilir ve disk dolduğunda sunucu sıkıntıya girebilir. logrotate zaten Debian’da kurulu geliyor, Nginx için konfigürasyonu kontrol edelim:
cat /etc/logrotate.d/nginx
Bu dosya genellikle kurulumla birlikte geliyor ama içeriğini gözden geçirmek iyi olur:
cat > /etc/logrotate.d/nginx << 'EOF'
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 nginx adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
EOF
Bu konfigürasyon logları günlük rotate ediyor, 14 gün saklıyor ve sıkıştırıyor. kill -USR1 Nginx’e log dosyasını yeniden açmasını söylüyor, servis kesintisi olmuyor.
Gerçek zamanlı log takibi için:
tail -f /var/log/nginx/access.log
tail -f /var/log/nginx/error.log
# Belirli bir IP'den gelen istekleri filtrele
tail -f /var/log/nginx/access.log | grep "192.168.1.100"
# 500 hatalarını izle
tail -f /var/log/nginx/access.log | grep " 500 "
Rate Limiting ile Brute Force Koruması
Nginx’in yerleşik rate limiting özelliği basit brute force saldırılarını ve DDoS’u hafifletmek için kullanılabiliyor. Ana nginx.conf’un http bloğuna şunu ekle:
# /etc/nginx/conf.d/rate-limit.conf
cat > /etc/nginx/conf.d/rate-limit.conf << 'EOF'
limit_req_zone $binary_remote_addr zone=genel:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=baglanti:10m;
EOF
Virtual host konfigürasyonunda bu zone’ları kullanmak için:
location / {
limit_req zone=genel burst=20 nodelay;
limit_conn baglanti 10;
try_files $uri $uri/ =404;
}
location /wp-login.php {
limit_req zone=login burst=3 nodelay;
try_files $uri $uri/ =404;
}
burst=20 değeri: Kısa süreliğine 20 ekstra isteğe izin veriyor. nodelay: Burst isteklerini geciktirmek yerine hemen işliyor ama limiti aşınca 429 döndürüyor.
Performans Tuning: Worker ve Buffer Ayarları
Sunucunun RAM ve CPU’suna göre bu değerleri ayarlamak gerekiyor. Genel kurallar:
- worker_processes: CPU çekirdek sayısına eşit veya “auto”
- worker_connections: Her worker’ın aynı anda kaç bağlantı tutacağı
- client_body_buffer_size: POST verisi için buffer
cat > /etc/nginx/conf.d/performance.conf << 'EOF'
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 1 32k;
postpone_output 1460;
open_file_cache max=1000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 5;
open_file_cache_errors off;
EOF
open_file_cache: Sık açılan dosyaların descriptor’larını cache’de tutuyor. Statik dosya sunan bir sunucuda büyük fark yaratıyor.
client_max_body_size: Dosya upload eden bir uygulama varsa bu değeri artırmak gerekiyor. Varsayılan 1MB çoğu zaman yetmiyor.
Nginx Servis Yönetimi
Günlük operasyonlarda sıkça kullanılan komutlar:
# Konfigurasyonu test et (her zaman once bunu calistir)
nginx -t
# Servisi yeniden yukle (kesintisiz)
systemctl reload nginx
# Servisi yeniden baslat (kisa kesinti olur)
systemctl restart nginx
# Servisi durdur/baslat
systemctl stop nginx
systemctl start nginx
# Nginx durumunu goster
systemctl status nginx
# Nginx process'lerini goster
ps aux | grep nginx
# Aktif baglantilari say
ss -tnp | grep nginx | wc -l
# Nginx'in dinledigi portlari goster
ss -tlnp | grep nginx
Reload ile restart arasındaki fark önemli. reload yeni konfigürasyonu yüklerken mevcut bağlantıları kesmez. restart ise tüm bağlantıları kesip sıfırdan başlar. Production’da neredeyse her zaman reload yeterli.
Sorun Giderme
Nginx’te sorun çıktığında ilk bakılacak yer her zaman error log:
# Son 50 satir hata logu
tail -n 50 /var/log/nginx/error.log
# Belirli bir keyword icin hata logu
grep "permission denied" /var/log/nginx/error.log
# Nginx konfigurasyonunu debug modda test et
nginx -T | head -100
Sık karşılaşılan sorunlar:
- Permission denied: Web root dizini veya dosyaların sahibi ve izinleri yanlış. nginx kullanıcısının okuma yetkisi olmalı.
- Address already in use: 80 veya 443 portu başka bir servis tarafından kullanılıyor.
ss -tlnp | grep :80ile kontrol et. - 502 Bad Gateway: Nginx arkasında bir uygulama sunucusu varsa (PHP-FPM, Node.js gibi) o servis çalışmıyor demek.
- 404 Not Found: try_files veya root direktifi yanlış yapılandırılmış.
Nginx modüllerini listelemek de sorun gidermede yardımcı olabiliyor:
nginx -V 2>&1 | tr -- '-' 'n' | grep module
Otomatik Sağlık Kontrolü
Production sunucusunda Nginx’in çalışıp çalışmadığını periyodik kontrol eden basit bir script hazırlamak iyi bir alışkanlık:
cat > /usr/local/bin/nginx-check.sh << 'EOF'
#!/bin/bash
if ! systemctl is-active --quiet nginx; then
echo "$(date): Nginx durdu, yeniden baslatiliyor..." >> /var/log/nginx-check.log
systemctl start nginx
echo "$(date): Nginx yeniden baslatildi." >> /var/log/nginx-check.log
fi
EOF
chmod +x /usr/local/bin/nginx-check.sh
# Her 5 dakikada bir calistir
echo "*/5 * * * * root /usr/local/bin/nginx-check.sh" > /etc/cron.d/nginx-check
Bu yaklaşım basit ama etkili. Daha karmaşık bir monitoring kurulumu için Prometheus + Nginx Exporter kombinasyonu düşünülebilir ama bu ayrı bir yazı konusu.
Kurulumu Doğrulamak
Her şeyin doğru çalıştığını test etmek için:
# HTTP response test
curl -I http://example.com
# HTTPS test
curl -I https://example.com
# SSL sertifika detaylari
curl -vI https://example.com 2>&1 | grep -E "expire|SSL|subject"
# Guvenlik basliklarini kontrol et
curl -I https://example.com 2>&1 | grep -E "Strict|X-Frame|X-Content"
# Nginx durum ozeti
nginx -V 2>&1 | head -5
systemctl status nginx --no-pager
SSL Labs’ın online test aracına (ssllabs.com/ssltest) sitenin adresini girerek SSL konfigürasyonunu kapsamlı şekilde test edebilirsin. A veya A+ almak hedef olmalı.
Sonuç
Bu yazıda Debian üzerinde sıfırdan başlayarak production’a hazır bir Nginx kurulumu yaptık. Sadece paketi kurup geçmek yerine neden bu adımları attığımızı ve her konfigürasyonun ne anlama geldiğini anlamaya çalıştık. Minimal yaklaşım başta fazla iş gibi görünse de ileride hem sorun gidermeyi hem de güvenlik yönetimini çok kolaylaştırıyor.
Özetlemek gerekirse yapılanlar:
- Official Nginx reposundan güncel sürüm kurulumu
- Optimize edilmiş ana konfigürasyon
- Virtual host yapılandırması
- Let’s Encrypt ile ücretsiz SSL
- Güvenlik başlıkları ve rate limiting
- Log yönetimi ve logrotate
- Performans tuning
Nginx’in gücü basitliğinde. Konfigürasyon dosyaları okunabilir, dokümantasyon mükemmel ve topluluk devasa. Temel kurulumu bu şekilde sağlam attıktan sonra PHP-FPM ile dinamik içerik sunma, reverse proxy olarak kullanma veya load balancing gibi konulara geçmek çok daha kolay hale geliyor.