SSL Termination ve HTTPS Yönetimi: Nginx Rehberi
Modern web altyapılarında SSL/TLS yönetimi artık bir lüks değil, zorunluluk. Kullanıcı verilerini korumak, SEO puanını artırmak ve tarayıcı uyarılarından kurtulmak için HTTPS kaçınılmaz. Peki ya bu SSL işlemlerini backend sunucularına bırakmak yerine Nginx’e devredersen? İşte “SSL Termination” denen kavram tam da bu. Bu yazıda Nginx ile SSL termination kurulumunu, sertifika yönetimini, güvenlik optimizasyonlarını ve gerçek dünya senaryolarını ele alacağız.
SSL Termination Nedir ve Neden Önemli?
SSL termination, şifrelenmiş HTTPS trafiğini Nginx’in “sonlandırması” ve backend sunuculara düz HTTP olarak iletmesi anlamına gelir. Bu yaklaşım birkaç kritik avantaj sağlar:
- Merkezi sertifika yönetimi: Onlarca backend sunucu yerine tek bir noktada sertifika güncellemesi
- Backend yükünü azaltma: Kriptografik işlemler CPU yoğun operasyonlardır, bunları Nginx’e bırakmak backend’lerin işini hafifletir
- Performans optimizasyonu: Nginx’in TLS stack’i son derece verimli çalışır
- Kolay troubleshooting: SSL sorunlarını tek bir katmanda debug edersin
Tabii bunun bir bedeli var: Nginx ile backend arasındaki trafik şifresiz gider. Eğer aynı sunucudaysanız ya da izole bir iç ağdasanız sorun değil. Ama farklı veri merkezleri arasında gidiyorsa, iç trafiği de şifrelemeni öneririm.
Let’s Encrypt ile Otomatik Sertifika Alma
Üretim ortamında çoğumuz Let’s Encrypt kullanıyoruz. Certbot ile bu iş oldukça basit.
# Certbot kurulumu (Ubuntu/Debian)
sudo apt update
sudo apt install certbot python3-certbot-nginx
# Nginx için otomatik sertifika al
sudo certbot --nginx -d example.com -d www.example.com
# Sadece sertifika al, Nginx konfig değiştirme (manuel yönetim tercih ediyorsan)
sudo certbot certonly --nginx -d example.com -d www.example.com
# Wildcard sertifika (DNS challenge gerektirir)
sudo certbot certonly --manual --preferred-challenges dns
-d "*.example.com" -d example.com
Certbot başarılı olursa sertifikalar /etc/letsencrypt/live/example.com/ altına düşer. Renewal için cron veya systemd timer zaten otomatik kurulur, ama test etmekte fayda var:
# Renewal simülasyonu
sudo certbot renew --dry-run
# Mevcut sertifikaları listele
sudo certbot certificates
# Systemd timer durumunu kontrol et
sudo systemctl status certbot.timer
Temel SSL Termination Yapılandırması
Şimdi asıl işe gelelim. Basit bir SSL termination konfigürasyonu şöyle görünür:
# /etc/nginx/sites-available/example.com
# HTTP'den HTTPS'e yönlendirme
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# Let's Encrypt challenge için bu lokasyon şart
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Geri kalanı HTTPS'e yönlendir
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS sunucusu
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name example.com www.example.com;
# SSL sertifikaları
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Güvenilir CA zinciri (OCSP stapling için)
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
# Backend'e proxy
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
X-Forwarded-Proto header’ı kritik. Backend uygulamaların gelen isteğin HTTP mi HTTPS mi olduğunu anlaması için buna ihtiyaçları var. Yoksa Django, Rails, Express gibi frameworkler HTTP olarak algılar ve redirect döngülerine girebilirsin.
SSL Güvenlik Optimizasyonları
Default SSL konfigürasyonu yeterince güvenli değildir. Mozilla SSL Configuration Generator’u referans alarak güvenli bir SSL yapılandırması hazırlamak iyi bir alışkanlık.
# /etc/nginx/snippets/ssl-params.conf
# Bu dosyayı tüm SSL sunucularında include edeceksin
# Protokol versiyonları - TLS 1.0 ve 1.1 artık kullanma
ssl_protocols TLSv1.2 TLSv1.3;
# Cipher suites - modern konfigürasyon
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# Server cipher tercihini aktif et
ssl_prefer_server_ciphers off;
# SSL session cache - performans için önemli
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# DH parametresi - zayıf DH saldırılarına karşı
ssl_dhparam /etc/nginx/dhparam.pem;
# OCSP Stapling - sertifika iptal kontrolünü hızlandırır
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# HSTS - 6 ay boyunca HTTPS'i zorla
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
# Ek güvenlik header'ları
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
DH parametresini oluşturmayı unutma:
# 2048 bit DH parametresi oluştur (biraz zaman alabilir)
sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048
# 4096 bit daha güvenli ama çok daha yavaş
# sudo openssl dhparam -out /etc/nginx/dhparam.pem 4096
Gerçek Dünya Senaryosu: Çok Domainli API Gateway
Diyelim ki birden fazla microservice’i tek Nginx üzerinden yönetiyorsun. Her servis farklı bir subdomain üzerinden erişilebilir. Buna genellikle “API Gateway” pattern diyoruz.
# /etc/nginx/nginx.conf içindeki http bloğuna ekle
upstream auth_service {
server 127.0.0.1:8001;
keepalive 32;
}
upstream user_service {
server 127.0.0.1:8002;
keepalive 32;
}
upstream payment_service {
server 127.0.0.1:8003;
server 127.0.0.1:8004; # Yük dengeleme için ikinci instance
keepalive 32;
}
# /etc/nginx/sites-available/api-gateway
# Wildcard SSL ile tüm subdomain'leri kapsa
server {
listen 443 ssl;
http2 on;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
include /etc/nginx/snippets/ssl-params.conf;
# Rate limiting - DDoS koruması
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/m;
# Auth service
location /auth/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://auth_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# Timeout ayarları
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# User service
location /users/ {
limit_req zone=api_limit burst=50 nodelay;
proxy_pass http://user_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
# Payment service - daha sıkı rate limit
location /payments/ {
limit_req_zone $binary_remote_addr zone=payment_limit:10m rate=10r/m;
limit_req zone=payment_limit burst=5 nodelay;
proxy_pass http://payment_service/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# Payment için daha uzun timeout
proxy_read_timeout 60s;
}
}
Sertifika Yenileme Otomasyonu
Production’da sertifika yenilemeyi unutmak felakettir. Bunu tam otomatik hale getirmek şart.
#!/bin/bash
# /usr/local/bin/renew-ssl.sh
# Sertifikaları yenile
certbot renew --quiet --no-self-upgrade
# Eğer yenileme başarılıysa Nginx'i reload et
if [ $? -eq 0 ]; then
# Nginx config test et, başarılıysa reload
nginx -t 2>/dev/null && systemctl reload nginx
# Başarı logu
echo "$(date): SSL sertifikaları başarıyla yenilendi" >> /var/log/ssl-renewal.log
else
# Hata bildirimi - buraya Slack webhook veya email ekleyebilirsin
echo "$(date): SSL yenileme BASARISIZ" >> /var/log/ssl-renewal.log
# Opsiyonel: Email bildirimi
# echo "SSL renewal failed on $(hostname)" | mail -s "SSL Alert" [email protected]
fi
# Script'i çalıştırılabilir yap
sudo chmod +x /usr/local/bin/renew-ssl.sh
# Crontab'a ekle - her gün 02:30'da çalışsın
sudo crontab -e
# Şu satırı ekle:
# 30 2 * * * /usr/local/bin/renew-ssl.sh
# Veya systemd timer kullan (daha modern yaklaşım)
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer
SSL Sertifikası İzleme ve Expiry Kontrolü
Sertifikanın ne zaman sona ereceğini takip etmek önemli. Şu script günlük çalıştırılabilir:
#!/bin/bash
# /usr/local/bin/check-ssl-expiry.sh
DOMAIN="example.com"
WARNING_DAYS=30
CRITICAL_DAYS=14
# Sertifikanın bitiş tarihini al
EXPIRY_DATE=$(echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2>/dev/null
| openssl x509 -noout -enddate 2>/dev/null
| cut -d= -f2)
if [ -z "$EXPIRY_DATE" ]; then
echo "CRITICAL: ${DOMAIN} sertifikasına bağlanılamadı!"
exit 2
fi
# Kalan gün sayısını hesapla
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $NOW_EPOCH) / 86400 ))
echo "Domain: ${DOMAIN}"
echo "Sertifika bitiş tarihi: ${EXPIRY_DATE}"
echo "Kalan gün: ${DAYS_LEFT}"
if [ $DAYS_LEFT -lt $CRITICAL_DAYS ]; then
echo "KRITIK: Sertifika ${DAYS_LEFT} gün içinde sona erecek!"
# Buraya acil bildirim ekle
elif [ $DAYS_LEFT -lt $WARNING_DAYS ]; then
echo "UYARI: Sertifika ${DAYS_LEFT} gün içinde sona erecek."
else
echo "OK: Sertifika ${DAYS_LEFT} gün geçerli."
fi
Mevcut SSL Konfigürasyonunu Test Etme
Konfigürasyonu yaptıktan sonra test etmeden geçme. Hem lokal hem de harici testler yap.
# Nginx config syntax kontrolü
sudo nginx -t
# Nginx'i reload et
sudo systemctl reload nginx
# SSL bağlantısını test et
openssl s_client -connect example.com:443 -servername example.com
# Hangi protokol ve cipher kullanıldığını gör
openssl s_client -connect example.com:443
-servername example.com
-tls1_3 2>/dev/null | grep "Protocol|Cipher"
# TLS 1.1'in reddedildiğini doğrula (çıktıda hata görmeli)
openssl s_client -connect example.com:443
-servername example.com
-tls1_1 2>/dev/null | grep "handshake failure|no peer certificate"
# HSTS header'ını kontrol et
curl -I https://example.com | grep -i "strict-transport"
# Sertifika detaylarını görüntüle
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null
| openssl x509 -noout -text | grep -E "Subject:|Issuer:|Not After"
SSL Labs gibi harici araçlar da işe yarar. https://www.ssllabs.com/ssltest/ adresine domainini girerek A+ not alıp almadığını kontrol edebilirsin. Yukarıdaki konfigürasyon uygulandığında A+ beklenmeli.
Sorun Giderme: Yaygın SSL Sorunları
Mixed Content Hataları
Backend uygulaması HTTP resource’ları render ediyorsa tarayıcı uyarı verir. Nginx seviyesinde bu sorunu azaltmak için:
# Content Security Policy header ekle
add_header Content-Security-Policy "upgrade-insecure-requests" always;
# Backend'e protokol bilgisini doğru ilet
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
Certificate Chain Sorunları
Eğer mobil tarayıcılar sertifikanı kabul etmiyorsa, büyük ihtimalle intermediate sertifika eksik. Let’s Encrypt’in fullchain.pem dosyası bu zinciri içerir, ama manuel sertifikalarda sık rastlanan sorun:
# Sertifika zincirini kontrol et
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt /etc/nginx/ssl/example.crt
# Zincirdeki sertifikaları listele
openssl crl2pkcs7 -nocrl -certfile /etc/nginx/ssl/fullchain.crt
| openssl pkcs7 -print_certs -noout
# Intermediate sertifikayı fullchain'e dahil et
cat example.crt intermediate.crt > fullchain.crt
SNI Sorunları
Birden fazla SSL sanal sunucu çalıştırıyorsan ve eski Nginx sürümlerinde sorun çıkıyorsa:
# Nginx'in SNI destekleyip desteklemediğini kontrol et
nginx -V 2>&1 | grep TLS
# SNI ile bağlantı testi
openssl s_client -connect example.com:443 -servername example.com -tlsextdebug 2>&1 | grep "TLS server extension"
Performans: SSL Session Resumption
Büyük trafik altında her HTTPS bağlantısı için tam handshake yapmak pahalıdır. Session resumption bunu önler:
# Session cache tüm worker'lar arasında paylaşılsın
ssl_session_cache shared:SSL:50m; # 50MB ~ 200.000 session
ssl_session_timeout 4h; # 4 saatlik session geçerliliği
# TLS 1.3 0-RTT - ilk veri transferini hızlandırır
# Dikkat: Replay attack riski var, sadece idempotent istekler için güvenli
ssl_early_data on;
# Early data için header ekle (backend bunu kontrol edebilir)
proxy_set_header Early-Data $ssl_early_data;
# Session resumption çalışıyor mu test et
openssl s_client -connect example.com:443 -servername example.com -reconnect 2>&1
| grep "Reused|New"
# "Reused" görüyorsan session resumption çalışıyor
Sonuç
SSL termination Nginx’in en değerli özelliklerinden biri. Doğru yapılandırıldığında hem güvenlik hem de performans açısından ciddi kazanımlar sağlıyor. Özetlemek gerekirse:
- Let’s Encrypt + Certbot kombinasyonu production için en pratik çözüm. Manuel sertifika yönetimini geride bırak.
- TLS 1.2/1.3 zorunlu, eski protokolleri devre dışı bırak. HSTS ile HTTPS’i tamamen zorla.
- OCSP Stapling açık olsun, sertifika doğrulama gecikmelerini önler.
- Session cache konfigürasyonu büyük trafiklerde ölçek farkı yaratır.
- Sertifika izleme script’i mutlaka çalışsın, aksi halde expiry sürpriz olur.
- SSL Labs testi her büyük değişiklikten sonra tekrar et.
Microservice mimarisinde Nginx’i API gateway olarak kullandığında SSL termination merkezi yönetimi kolaylaştırır. Her servis için ayrı sertifika ve TLS konfigürasyonu yerine tek noktada halledersin. Bu hem operasyonel yükü azaltır hem de tutarlı güvenlik politikası uygulamanı sağlar.
Son olarak şunu söylemek isterim: SSL konfigürasyonu “kur ve unut” değil. TLS vulnerabilityleri çıkmaya devam ediyor, cipher suite önerileri değişiyor. Mozilla SSL Configuration Generator ve SSL Labs’i periyodik olarak kontrol et, konfigürasyonunu güncel tut.
