Minimal Web Sunucu Kurulumu: Debian Üzerinde Nginx

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 :80 ile 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.

Yorum yapın